jawere 1.4.0 → 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
@@ -13339,6 +13339,30 @@ var TOOL_DEFS = [
13339
13339
  }
13340
13340
  }
13341
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
+ },
13342
13366
  {
13343
13367
  type: "function",
13344
13368
  function: {
@@ -13757,6 +13781,14 @@ async function statTool(path, workDir) {
13757
13781
  return `Error: [stat-failed] ${fullPath}: ${err.message}`;
13758
13782
  }
13759
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
+ }
13760
13792
  async function executeTool(call, workDir) {
13761
13793
  const { id, function: fn } = call;
13762
13794
  let args;
@@ -13799,6 +13831,14 @@ async function executeTool(call, workDir) {
13799
13831
  case "find":
13800
13832
  result = await findTool(args.pattern, args.path, workDir);
13801
13833
  break;
13834
+ case "diff":
13835
+ result = await diffTool(
13836
+ args.path,
13837
+ args.staged,
13838
+ args.base,
13839
+ workDir
13840
+ );
13841
+ break;
13802
13842
  case "grep":
13803
13843
  result = await grepTool(
13804
13844
  args.pattern,
@@ -14265,13 +14305,14 @@ async function listSessions(convexUrl) {
14265
14305
 
14266
14306
  // src/agent.ts
14267
14307
  var MAX_TURNS = 500;
14268
- var MAX_OUTPUT_TOKENS = 3e5;
14308
+ var MAX_OUTPUT_TOKENS = 393216;
14269
14309
  var COL = () => process.stdout.columns || 80;
14270
14310
  var GRUVBOX_GREEN = "\x1B[38;2;184;187;3m";
14271
14311
  var GRUVBOX_GRAY = "\x1B[38;2;146;131;116m";
14272
14312
  var GRUVBOX_RED = "\x1B[38;2;251;73;52m";
14273
14313
  var RESET2 = "\x1B[0m";
14274
14314
  var FILE_TOOLS = /* @__PURE__ */ new Set(["read", "write", "edit", "stat"]);
14315
+ var SILENT_TOOLS = /* @__PURE__ */ new Set(["diff"]);
14275
14316
  function toolDetail(name, args) {
14276
14317
  switch (name) {
14277
14318
  case "read": {
@@ -14296,6 +14337,7 @@ function toolDetail(name, args) {
14296
14337
  }
14297
14338
  }
14298
14339
  function displayToolLine(name, args, ok, spin) {
14340
+ if (SILENT_TOOLS.has(name)) return;
14299
14341
  if (spin?.running) {
14300
14342
  spin.stop();
14301
14343
  }
@@ -14463,86 +14505,120 @@ async function runAgent(userMessage, options = {}) {
14463
14505
  return { text: msg2, sessionId, history: messages };
14464
14506
  }
14465
14507
  spin.update("Thinking\u2026");
14466
- const response = await withRetry(() => {
14467
- return client.chat.completions.create({
14468
- model: config.model,
14469
- messages,
14470
- tools: TOOL_DEFS,
14471
- tool_choice: "auto",
14472
- temperature: 0.2,
14473
- max_tokens: MAX_OUTPUT_TOKENS,
14474
- // Enable thinking/reasoning — DeepSeek specific params
14475
- ...{
14476
- thinking: { type: "enabled" },
14477
- reasoning_effort: "max"
14478
- }
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
+ });
14479
14528
  });
14480
- }).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) {
14481
14560
  if (err.name === "AbortError" || err.name === "Canceled") {
14482
- return null;
14561
+ spin.stop();
14562
+ const msg2 = "\n[Cancelled by user]";
14563
+ return { text: msg2, sessionId, history: messages };
14483
14564
  }
14484
14565
  throw err;
14485
- });
14486
- if (!response) {
14566
+ }
14567
+ if (options.signal?.aborted) {
14487
14568
  spin.stop();
14488
14569
  const msg2 = "\n[Cancelled by user]";
14489
14570
  return { text: msg2, sessionId, history: messages };
14490
14571
  }
14491
- const choice = response.choices[0];
14492
- if (!choice) {
14493
- spin.stop();
14494
- safeCall(
14495
- () => appendAssistantMessage(config.convexUrl, sessionId, "(error: no response)", null),
14496
- "appendAssistantMessage"
14497
- );
14498
- return { text: "Error: No response from model.", sessionId, history: messages };
14499
- }
14500
- const { message } = choice;
14501
- const usage = response.usage ? {
14502
- input: response.usage.prompt_tokens || 0,
14503
- output: response.usage.completion_tokens || 0,
14504
- total: response.usage.total_tokens || 0
14505
- } : void 0;
14506
- if (message.tool_calls && message.tool_calls.length > 0) {
14507
- 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) => ({
14508
14576
  id: tc.id,
14509
- name: tc.function.name,
14510
- arguments: tc.function.arguments
14577
+ name: tc.name,
14578
+ arguments: tc.arguments
14511
14579
  }));
14512
14580
  safeCall(
14513
14581
  () => appendAssistantMessage(
14514
14582
  config.convexUrl,
14515
14583
  sessionId,
14516
- message.content || null,
14584
+ streamedContent || null,
14517
14585
  toolCallsMeta.length > 0 ? toolCallsMeta : null,
14518
- usage
14586
+ streamUsage
14519
14587
  ),
14520
14588
  "appendAssistantMessage"
14521
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
+ }));
14522
14598
  messages.push({
14523
14599
  role: "assistant",
14524
- content: message.content || null,
14525
- tool_calls: message.tool_calls
14600
+ content: streamedContent || null,
14601
+ tool_calls: openaiToolCalls
14526
14602
  });
14527
- for (const tc of message.tool_calls) {
14603
+ for (const tc of toolCallArray) {
14528
14604
  let args = {};
14529
14605
  try {
14530
- args = JSON.parse(tc.function.arguments);
14606
+ args = JSON.parse(tc.arguments);
14531
14607
  } catch {
14532
14608
  }
14533
- spin.update(`Running ${tc.function.name}\u2026`);
14609
+ spin.update(`Running ${tc.name}\u2026`);
14534
14610
  const result = await executeTool(
14535
14611
  {
14536
14612
  id: tc.id,
14537
14613
  function: {
14538
- name: tc.function.name,
14539
- arguments: tc.function.arguments
14614
+ name: tc.name,
14615
+ arguments: tc.arguments
14540
14616
  }
14541
14617
  },
14542
14618
  config.workDir
14543
14619
  );
14544
14620
  const ok = !result.content.startsWith("Error");
14545
- displayToolLine(tc.function.name, args, ok, spin);
14621
+ displayToolLine(tc.name, args, ok, spin);
14546
14622
  messages.push({
14547
14623
  role: "tool",
14548
14624
  tool_call_id: result.tool_call_id,
@@ -14554,7 +14630,7 @@ async function runAgent(userMessage, options = {}) {
14554
14630
  config.convexUrl,
14555
14631
  sessionId,
14556
14632
  result.tool_call_id,
14557
- tc.function.name,
14633
+ tc.name,
14558
14634
  result.content,
14559
14635
  isError
14560
14636
  ),
@@ -14564,10 +14640,9 @@ async function runAgent(userMessage, options = {}) {
14564
14640
  continue;
14565
14641
  }
14566
14642
  spin.stop();
14567
- const rawText = message.content || "";
14568
- const text = stripThinking(rawText) || "(empty response)";
14643
+ const text = stripThinking(streamedContent) || "(empty response)";
14569
14644
  safeCall(
14570
- () => appendAssistantMessage(config.convexUrl, sessionId, text, null, usage),
14645
+ () => appendAssistantMessage(config.convexUrl, sessionId, text, null, streamUsage),
14571
14646
  "appendAssistantMessage"
14572
14647
  );
14573
14648
  console.log("");
@@ -14871,20 +14946,32 @@ function getProjectInfo(workDir) {
14871
14946
  }
14872
14947
  return { name, version };
14873
14948
  }
14874
- async function scanCodebase(workDir) {
14949
+ async function scanCodebase(workDir, onProgress) {
14875
14950
  const codebaseDir = resolve2(workDir, CODEBASE_DIR);
14876
14951
  await mkdir3(codebaseDir, { recursive: true });
14952
+ onProgress?.("listing files");
14877
14953
  const allFiles = await getAllFiles(workDir, workDir);
14878
14954
  const files = allFiles.sort();
14955
+ onProgress?.(`found ${files.length} files`);
14956
+ onProgress?.("building tree");
14879
14957
  const tree = buildTree(files);
14958
+ onProgress?.(`summarizing ${files.length} files`);
14880
14959
  const summaries = {};
14881
- for (const file of files) {
14882
- 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]));
14883
14963
  if (summary) {
14884
- 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;
14885
14970
  }
14886
14971
  }
14972
+ onProgress?.("reading project info");
14887
14973
  const { name, version } = getProjectInfo(workDir);
14974
+ onProgress?.("hashing files");
14888
14975
  const gitHash = await getGitHash(workDir);
14889
14976
  const checksums = {
14890
14977
  scannedAt: Date.now(),
@@ -14902,9 +14989,12 @@ async function scanCodebase(workDir) {
14902
14989
  }
14903
14990
  }
14904
14991
  }
14992
+ onProgress?.("writing checksums");
14905
14993
  await writeFile3(resolve2(workDir, CHECKSUMS_FILE), JSON.stringify(checksums, null, 2) + "\n", "utf-8");
14994
+ onProgress?.("writing tree.yaml");
14906
14995
  const treeYaml = generateTreeYaml(name, version, tree, summaries);
14907
14996
  await writeFile3(resolve2(workDir, TREE_FILE), treeYaml, "utf-8");
14997
+ onProgress?.("writing meta.json");
14908
14998
  const meta = {
14909
14999
  scannedAt: Date.now(),
14910
15000
  fileCount: files.length,
@@ -14992,11 +15082,18 @@ async function runScanner(workDir, force = false) {
14992
15082
  const meta = JSON.parse(
14993
15083
  await readFile3(resolve2(workDir, META_FILE), "utf-8")
14994
15084
  );
15085
+ process.stderr.write(` (cached)`);
14995
15086
  return { fileCount: meta.fileCount, cached: true };
14996
15087
  } catch {
14997
15088
  }
14998
15089
  }
14999
- 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`);
15000
15097
  return { fileCount, cached: false };
15001
15098
  }
15002
15099
 
@@ -15351,10 +15448,10 @@ async function main() {
15351
15448
  try {
15352
15449
  const scanResult = await runScanner(config.workDir);
15353
15450
  if (scanResult.cached) {
15354
- process.stderr.write(` ${G_GRAY2}(cached, ${scanResult.fileCount} files)${R3}
15451
+ process.stderr.write(` ${G_GRAY2}(${scanResult.fileCount} files)${R3}
15355
15452
  `);
15356
15453
  } else {
15357
- process.stderr.write(` ${G_GRAY2}(${scanResult.fileCount} files)${R3}
15454
+ process.stderr.write(`${G_GRAY2}Done \u2014 ${scanResult.fileCount} files indexed${R3}
15358
15455
  `);
15359
15456
  }
15360
15457
  } catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jawere",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "jawere": "bin/jawere.js"
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>;