@harperfast/agent 0.11.5 → 0.12.1

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 (2) hide show
  1. package/dist/agent.js +428 -298
  2. package/package.json +4 -3
package/dist/agent.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  // agent.ts
4
4
  import "dotenv/config";
5
- import { Agent as Agent3, run as run2 } from "@openai/agents";
5
+ import { Agent as Agent2, run as run2 } from "@openai/agents";
6
6
  import chalk13 from "chalk";
7
7
 
8
8
  // lifecycle/cleanUpAndSayBye.ts
@@ -302,7 +302,6 @@ function harperResponse(text) {
302
302
  import process2 from "process";
303
303
 
304
304
  // lifecycle/trackedState.ts
305
- import "@openai/agents";
306
305
  var trackedState = {
307
306
  agent: null,
308
307
  cwd: process.cwd(),
@@ -315,7 +314,8 @@ var trackedState = {
315
314
  sessionPath: null,
316
315
  useFlexTier: false,
317
316
  disableSpinner: false,
318
- enableInterruptions: true
317
+ enableInterruptions: true,
318
+ session: null
319
319
  };
320
320
 
321
321
  // utils/shell/spinner.ts
@@ -469,6 +469,9 @@ function normalizeOllamaBaseUrl(baseUrl) {
469
469
  return urlObj.toString().replace(/\/$/, "");
470
470
  }
471
471
 
472
+ // lifecycle/parseArgs.ts
473
+ import path from "path";
474
+
472
475
  // utils/shell/cli.ts
473
476
  import chalk3 from "chalk";
474
477
 
@@ -628,6 +631,10 @@ function parseArgs() {
628
631
  if (!trackedState.sessionPath && process.env.HARPER_AGENT_SESSION) {
629
632
  trackedState.sessionPath = process.env.HARPER_AGENT_SESSION;
630
633
  }
634
+ const sp = trackedState.sessionPath;
635
+ if (sp) {
636
+ trackedState.sessionPath = sp && !sp.startsWith("~") && !path.isAbsolute(sp) ? path.resolve(process.cwd(), sp) : sp;
637
+ }
631
638
  if (!trackedState.useFlexTier && isTrue(process.env.HARPER_AGENT_FLEX_TIER)) {
632
639
  trackedState.useFlexTier = true;
633
640
  }
@@ -696,9 +703,9 @@ function sayHi() {
696
703
  }
697
704
 
698
705
  // tools/files/applyPatchTool.ts
699
- import { tool } from "@openai/agents";
706
+ import { tool as tool2 } from "@openai/agents";
700
707
  import chalk7 from "chalk";
701
- import { z } from "zod";
708
+ import { z as z2 } from "zod";
702
709
 
703
710
  // utils/files/printDiff.ts
704
711
  import chalk5 from "chalk";
@@ -738,11 +745,68 @@ function getEnv(newKey, oldKey) {
738
745
  return void 0;
739
746
  }
740
747
 
748
+ // tools/harper/getHarperSkillTool.ts
749
+ import { tool } from "@openai/agents";
750
+ import { readdirSync, readFileSync as readFileSync3 } from "fs";
751
+ import { createRequire } from "module";
752
+ import { dirname, join as join6 } from "path";
753
+ import { z } from "zod";
754
+ var createHarper = dirname(createRequire(import.meta.url).resolve("create-harper"));
755
+ var agentsMarkdown = join6(
756
+ createHarper,
757
+ "AGENTS.md"
758
+ );
759
+ var skillsDir = join6(
760
+ createHarper,
761
+ "template-vanilla",
762
+ "skills"
763
+ );
764
+ var skillLinkRegex = /\[[^\]]+]\(skills\/([^)]+)\.md\)/g;
765
+ var skills = getSkills();
766
+ var ToolParameters = z.object({
767
+ skill: z.enum(skills.length > 0 ? skills : ["none"]).describe(
768
+ "The name of the skill to retrieve."
769
+ )
770
+ });
771
+ var getHarperSkillTool = tool({
772
+ name: "getHarperSkill",
773
+ description: getSkillsDescription(),
774
+ parameters: ToolParameters,
775
+ execute
776
+ });
777
+ function getSkillsDescription() {
778
+ try {
779
+ return readFileSync3(agentsMarkdown, "utf8").replace("This repository contains", "This tool describes").replace(skillLinkRegex, "$1");
780
+ } catch {
781
+ return "Returns the contents of a Harper skill markdown file. Skills provide guidance on developing Harper applications.";
782
+ }
783
+ }
784
+ function getSkills() {
785
+ try {
786
+ return readdirSync(skillsDir).filter((file) => file.endsWith(".md")).map((file) => file.replace(".md", ""));
787
+ } catch {
788
+ return [];
789
+ }
790
+ }
791
+ async function execute({ skill }) {
792
+ if (skill === "none") {
793
+ return "No skills found.";
794
+ }
795
+ try {
796
+ const filePath = join6(skillsDir, `${skill}.md`);
797
+ const content = readFileSync3(filePath, "utf8");
798
+ trackedState.session?.addSkillRead?.(skill);
799
+ return content;
800
+ } catch (error) {
801
+ return `Error reading Harper skill "${skill}": ${error}`;
802
+ }
803
+ }
804
+
741
805
  // tools/files/workspaceEditor.ts
742
806
  import { applyDiff } from "@openai/agents";
743
- import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
807
+ import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
744
808
  import { mkdir, rm, writeFile } from "fs/promises";
745
- import path3 from "path";
809
+ import path4 from "path";
746
810
 
747
811
  // utils/files/normalizeDiff.ts
748
812
  function normalizeDiff(diff) {
@@ -759,17 +823,17 @@ function normalizeDiff(diff) {
759
823
  }
760
824
 
761
825
  // utils/files/paths.ts
762
- import path2 from "path";
826
+ import path3 from "path";
763
827
 
764
828
  // utils/files/aiignore.ts
765
- import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
766
- import path from "path";
829
+ import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
830
+ import path2 from "path";
767
831
  var ignorePatterns = [];
768
832
  function loadAiIgnore() {
769
- const ignorePath = path.join(trackedState.cwd, ".aiignore");
833
+ const ignorePath = path2.join(trackedState.cwd, ".aiignore");
770
834
  if (existsSync4(ignorePath)) {
771
835
  try {
772
- const content = readFileSync3(ignorePath, "utf8");
836
+ const content = readFileSync4(ignorePath, "utf8");
773
837
  ignorePatterns = content.split(/\r?\n/).map((line) => line.trim()).filter((line) => line && !line.startsWith("#")).map((pattern) => pattern.endsWith("/") || pattern.endsWith("\\") ? pattern.slice(0, -1) : pattern);
774
838
  } catch (error) {
775
839
  console.error(`Error reading .aiignore: ${error}`);
@@ -780,39 +844,39 @@ function loadAiIgnore() {
780
844
  }
781
845
  }
782
846
  function isIgnored(filePath) {
783
- const directParts = path.normalize(filePath).split(path.sep);
847
+ const directParts = path2.normalize(filePath).split(path2.sep);
784
848
  if (directParts.includes(".aiignore")) {
785
849
  return true;
786
850
  }
787
- const absolutePath = path.resolve(trackedState.cwd, filePath);
788
- const relativePath = path.relative(trackedState.cwd, absolutePath);
789
- if (relativePath.startsWith("..") || path.isAbsolute(relativePath)) {
851
+ const absolutePath = path2.resolve(trackedState.cwd, filePath);
852
+ const relativePath = path2.relative(trackedState.cwd, absolutePath);
853
+ if (relativePath.startsWith("..") || path2.isAbsolute(relativePath)) {
790
854
  return false;
791
855
  }
792
- const normalizedPath = path.normalize(relativePath);
793
- const parts = normalizedPath.split(path.sep);
856
+ const normalizedPath2 = path2.normalize(relativePath);
857
+ const parts = normalizedPath2.split(path2.sep);
794
858
  loadAiIgnore();
795
859
  if (ignorePatterns.length === 0) {
796
860
  return false;
797
861
  }
798
862
  return ignorePatterns.some((pattern) => {
799
863
  if (pattern.startsWith("/")) {
800
- const normalizedPattern = path.normalize(pattern.substring(1));
801
- return normalizedPath === normalizedPattern || normalizedPath.startsWith(normalizedPattern + path.sep);
864
+ const normalizedPattern = path2.normalize(pattern.substring(1));
865
+ return normalizedPath2 === normalizedPattern || normalizedPath2.startsWith(normalizedPattern + path2.sep);
802
866
  }
803
867
  return parts.some((part, index) => {
804
- const subPath = parts.slice(index).join(path.sep);
805
- const normalizedPattern = path.normalize(pattern);
806
- return subPath === normalizedPattern || subPath.startsWith(normalizedPattern + path.sep);
868
+ const subPath = parts.slice(index).join(path2.sep);
869
+ const normalizedPattern = path2.normalize(pattern);
870
+ return subPath === normalizedPattern || subPath.startsWith(normalizedPattern + path2.sep);
807
871
  });
808
872
  });
809
873
  }
810
874
 
811
875
  // utils/files/paths.ts
812
876
  function resolvePath(root, relativePath) {
813
- const resolved = path2.resolve(root, relativePath);
814
- const relative = path2.relative(root, resolved);
815
- if (relative.startsWith("..") || path2.isAbsolute(relative)) {
877
+ const resolved = path3.resolve(root, relativePath);
878
+ const relative = path3.relative(root, resolved);
879
+ if (relative.startsWith("..") || path3.isAbsolute(relative)) {
816
880
  throw new Error(`Operation outside workspace: ${relativePath}`);
817
881
  }
818
882
  if (isIgnored(resolved)) {
@@ -830,7 +894,7 @@ var WorkspaceEditor = class {
830
894
  async createFile(operation) {
831
895
  try {
832
896
  const targetPath = resolvePath(this.root(), operation.path);
833
- await mkdir(path3.dirname(targetPath), { recursive: true });
897
+ await mkdir(path4.dirname(targetPath), { recursive: true });
834
898
  const normalizedDiff = normalizeDiff(operation.diff);
835
899
  const content = applyDiff("", normalizedDiff, "create");
836
900
  await writeFile(targetPath, content, "utf8");
@@ -845,7 +909,7 @@ var WorkspaceEditor = class {
845
909
  if (!existsSync5(targetPath)) {
846
910
  return { status: "failed", output: "Error: file not found at path " + targetPath };
847
911
  }
848
- const original = readFileSync4(targetPath, "utf8");
912
+ const original = readFileSync5(targetPath, "utf8");
849
913
  const normalizedDiff = normalizeDiff(operation.diff);
850
914
  const patched = applyDiff(original, normalizedDiff);
851
915
  await writeFile(targetPath, patched, "utf8");
@@ -869,48 +933,96 @@ var WorkspaceEditor = class {
869
933
  };
870
934
 
871
935
  // tools/files/applyPatchTool.ts
872
- var ApplyPatchParameters = z.object({
873
- type: z.enum(["create_file", "update_file", "delete_file"]).describe("The type of operation to perform."),
874
- path: z.string().describe("The path to the file to operate on."),
875
- diff: z.string().optional().default("").describe(
936
+ var ApplyPatchParameters = z2.object({
937
+ type: z2.enum(["create_file", "update_file", "delete_file"]).describe("The type of operation to perform."),
938
+ path: z2.string().describe("The path to the file to operate on."),
939
+ diff: z2.string().optional().default("").describe(
876
940
  'The diff to apply. For create_file, every line must start with "+". For update_file, use a headerless unified diff format (start sections with "@@", and use "+", "-", or " " for lines). Do not include markers like "*** Begin Patch" or "*** Add File:".'
877
941
  )
878
942
  });
943
+ function normalizedPath(p) {
944
+ return p.replace(/\\/g, "/").replace(/^\.\/?/, "");
945
+ }
946
+ async function getSkillsRead() {
947
+ try {
948
+ const s = trackedState.session;
949
+ const arr = await Promise.resolve(s?.getSkillsRead?.());
950
+ return Array.isArray(arr) ? arr : [];
951
+ } catch {
952
+ return [];
953
+ }
954
+ }
955
+ function pickExistingSkill(candidates) {
956
+ for (const c of candidates) {
957
+ if (skills.includes(c)) {
958
+ return c;
959
+ }
960
+ }
961
+ return null;
962
+ }
963
+ async function requiredSkillForOperation(path8, type) {
964
+ if (type === "delete_file") {
965
+ return null;
966
+ }
967
+ const p = normalizedPath(path8);
968
+ const read = await getSkillsRead();
969
+ if (p.includes("/resources/") || p.startsWith("resources/")) {
970
+ if (!read.includes("custom-resources") && !read.includes("extending-tables")) {
971
+ return pickExistingSkill(["custom-resources", "extending-tables"]);
972
+ }
973
+ }
974
+ if (p.includes("/schemas/") || p.startsWith("schemas/") || p.includes("/schema/") || p.startsWith("schema/")) {
975
+ if (!read.includes("adding-tables-with-schemas")) {
976
+ return pickExistingSkill(["adding-tables-with-schemas"]);
977
+ }
978
+ }
979
+ return null;
980
+ }
981
+ var editor = new WorkspaceEditor(() => trackedState.cwd);
982
+ async function needsApproval(runContext, operation, callId) {
983
+ try {
984
+ const needed = await requiredSkillForOperation(operation.path, operation.type);
985
+ if (needed) {
986
+ return false;
987
+ }
988
+ if (callId && runContext.isToolApproved({ toolName: "apply_patch", callId })) {
989
+ return false;
990
+ }
991
+ const autoApproved = getEnv("HARPER_AGENT_AUTO_APPROVE_PATCHES", "APPLY_PATCH_AUTO_APPROVE") === "1";
992
+ spinner.stop();
993
+ if (autoApproved) {
994
+ console.log(`
995
+ ${chalk7.bold.bgGreen.black(" Apply patch (auto-approved): ")}`);
996
+ } else {
997
+ console.log(`
998
+ ${chalk7.bold.bgYellow.black(" Apply patch approval required: ")}`);
999
+ }
1000
+ console.log(`${chalk7.bold(operation.type)}: ${operation.path}`);
1001
+ if (operation.diff) {
1002
+ printDiff(operation.diff);
1003
+ }
1004
+ if (autoApproved) {
1005
+ spinner.start();
1006
+ }
1007
+ return !autoApproved;
1008
+ } catch (err) {
1009
+ console.error("apply_patch approval step failed:", err);
1010
+ return false;
1011
+ }
1012
+ }
879
1013
  function createApplyPatchTool() {
880
- const editor = new WorkspaceEditor(() => trackedState.cwd);
881
- return tool({
1014
+ return tool2({
882
1015
  name: "apply_patch",
883
1016
  description: "Applies a patch (create, update, or delete a file) to the workspace.",
884
1017
  parameters: ApplyPatchParameters,
885
- needsApproval: async (runContext, operation, callId) => {
886
- try {
887
- if (callId && runContext.isToolApproved({ toolName: "apply_patch", callId })) {
888
- return false;
889
- }
890
- const autoApproved = getEnv("HARPER_AGENT_AUTO_APPROVE_PATCHES", "APPLY_PATCH_AUTO_APPROVE") === "1";
891
- spinner.stop();
892
- if (autoApproved) {
893
- console.log(`
894
- ${chalk7.bold.bgGreen.black(" Apply patch (auto-approved): ")}`);
895
- } else {
896
- console.log(`
897
- ${chalk7.bold.bgYellow.black(" Apply patch approval required: ")}`);
898
- }
899
- console.log(`${chalk7.bold(operation.type)}: ${operation.path}`);
900
- if (operation.diff) {
901
- printDiff(operation.diff);
902
- }
903
- if (autoApproved) {
904
- spinner.start();
905
- }
906
- return !autoApproved;
907
- } catch (err) {
908
- console.error("apply_patch approval step failed:", err);
909
- return false;
910
- }
911
- },
1018
+ needsApproval,
912
1019
  execute: async (operation) => {
913
1020
  try {
1021
+ const needed = await requiredSkillForOperation(operation.path, operation.type);
1022
+ if (needed) {
1023
+ const content = await execute({ skill: needed });
1024
+ return { status: "completed", output: content };
1025
+ }
914
1026
  switch (operation.type) {
915
1027
  case "create_file":
916
1028
  if (!operation.diff) {
@@ -936,15 +1048,15 @@ ${chalk7.bold.bgYellow.black(" Apply patch approval required: ")}`);
936
1048
  }
937
1049
 
938
1050
  // tools/files/changeCwdTool.ts
939
- import { tool as tool2 } from "@openai/agents";
1051
+ import { tool as tool3 } from "@openai/agents";
940
1052
  import { statSync } from "fs";
941
- import { z as z2 } from "zod";
942
- var ToolParameters = z2.object({
943
- path: z2.string().describe("Directory to switch into. Can be absolute or relative to current workspace.")
1053
+ import { z as z3 } from "zod";
1054
+ var ToolParameters2 = z3.object({
1055
+ path: z3.string().describe("Directory to switch into. Can be absolute or relative to current workspace.")
944
1056
  });
945
- async function execute({ path: path7 }) {
1057
+ async function execute2({ path: path8 }) {
946
1058
  try {
947
- const target = resolvePath(trackedState.cwd, path7);
1059
+ const target = resolvePath(trackedState.cwd, path8);
948
1060
  const stat = statSync(target);
949
1061
  if (!stat.isDirectory()) {
950
1062
  console.log(`Path is not a directory: ${target}`);
@@ -958,7 +1070,7 @@ async function execute({ path: path7 }) {
958
1070
  if (trackedState.agent) {
959
1071
  trackedState.agent.instructions = agentsMDContents;
960
1072
  }
961
- console.log("Detected AGENTS.md, reading its contents for the AI.");
1073
+ console.log("Detected AGENTS.md, reading its contents.");
962
1074
  return `Switched current working directory to ${trackedState.cwd}, with a AGENTS.md file containing:
963
1075
  ${agentsMDContents}
964
1076
  I strongly suggest you use these newfound skills!`;
@@ -969,27 +1081,27 @@ I strongly suggest you use these newfound skills!`;
969
1081
  return `Failed to change directory: ${err.message}`;
970
1082
  }
971
1083
  }
972
- var changeCwdTool = tool2({
1084
+ var changeCwdTool = tool3({
973
1085
  name: "changeCwd",
974
1086
  description: "Changes the current working directory for subsequent tools. Accepts absolute or relative paths.",
975
- parameters: ToolParameters,
976
- execute
1087
+ parameters: ToolParameters2,
1088
+ execute: execute2
977
1089
  });
978
1090
 
979
1091
  // tools/files/egrepTool.ts
980
- import { tool as tool3 } from "@openai/agents";
1092
+ import { tool as tool4 } from "@openai/agents";
981
1093
  import { execFile } from "child_process";
982
1094
  import { promisify } from "util";
983
- import { z as z3 } from "zod";
1095
+ import { z as z4 } from "zod";
984
1096
  var execFileAsync = promisify(execFile);
985
- var ToolParameters2 = z3.object({
986
- path: z3.string().describe("The path to start the search from."),
987
- pattern: z3.string().describe("The pattern to search")
1097
+ var ToolParameters3 = z4.object({
1098
+ path: z4.string().describe("The path to start the search from."),
1099
+ pattern: z4.string().describe("The pattern to search")
988
1100
  });
989
- var egrepTool = tool3({
1101
+ var egrepTool = tool4({
990
1102
  name: "egrep",
991
1103
  description: "File pattern searcher.",
992
- parameters: ToolParameters2,
1104
+ parameters: ToolParameters3,
993
1105
  async execute({ path: searchPath, pattern }) {
994
1106
  try {
995
1107
  const resolvedPath = resolvePath(trackedState.cwd, searchPath);
@@ -1015,21 +1127,21 @@ var egrepTool = tool3({
1015
1127
  });
1016
1128
 
1017
1129
  // tools/files/findTool.ts
1018
- import { tool as tool4 } from "@openai/agents";
1130
+ import { tool as tool5 } from "@openai/agents";
1019
1131
  import { execFile as execFile2 } from "child_process";
1020
1132
  import { promisify as promisify2 } from "util";
1021
- import { z as z4 } from "zod";
1133
+ import { z as z5 } from "zod";
1022
1134
  var execFileAsync2 = promisify2(execFile2);
1023
- var ToolParameters3 = z4.object({
1024
- path: z4.string().describe("The path to start the search from."),
1025
- iname: z4.string().describe(
1135
+ var ToolParameters4 = z5.object({
1136
+ path: z5.string().describe("The path to start the search from."),
1137
+ iname: z5.string().describe(
1026
1138
  "Case insensitive, true if the last component of the pathname being examined matches pattern. Special shell pattern matching characters (\u201C[\u201D, \u201C]\u201D, \u201C*\u201D, and \u201C?\u201D) may be used as part of pattern. These characters may be matched explicitly by escaping them with a backslash (\u201C\\\u201D)."
1027
1139
  )
1028
1140
  });
1029
- var findTool = tool4({
1141
+ var findTool = tool5({
1030
1142
  name: "find",
1031
1143
  description: "Walk a file hierarchy.",
1032
- parameters: ToolParameters3,
1144
+ parameters: ToolParameters4,
1033
1145
  async execute({ path: searchPath, iname }) {
1034
1146
  try {
1035
1147
  const resolvedPath = resolvePath(trackedState.cwd, searchPath);
@@ -1042,22 +1154,22 @@ var findTool = tool4({
1042
1154
  });
1043
1155
 
1044
1156
  // tools/files/readDirTool.ts
1045
- import { tool as tool5 } from "@openai/agents";
1157
+ import { tool as tool6 } from "@openai/agents";
1046
1158
  import { readdir } from "fs/promises";
1047
- import path4 from "path";
1048
- import { z as z5 } from "zod";
1049
- var ToolParameters4 = z5.object({
1050
- directoryName: z5.string().describe("The name of the directory to read.")
1159
+ import path5 from "path";
1160
+ import { z as z6 } from "zod";
1161
+ var ToolParameters5 = z6.object({
1162
+ directoryName: z6.string().describe("The name of the directory to read.")
1051
1163
  });
1052
- var readDirTool = tool5({
1164
+ var readDirTool = tool6({
1053
1165
  name: "readDir",
1054
1166
  description: "Lists the files in a directory.",
1055
- parameters: ToolParameters4,
1167
+ parameters: ToolParameters5,
1056
1168
  async execute({ directoryName }) {
1057
1169
  try {
1058
1170
  const resolvedPath = resolvePath(trackedState.cwd, directoryName);
1059
1171
  const files = await readdir(resolvedPath, "utf8");
1060
- return files.filter((file) => !isIgnored(path4.join(resolvedPath, file)));
1172
+ return files.filter((file) => !isIgnored(path5.join(resolvedPath, file)));
1061
1173
  } catch (error) {
1062
1174
  return `Error reading directory: ${error}`;
1063
1175
  }
@@ -1065,18 +1177,23 @@ var readDirTool = tool5({
1065
1177
  });
1066
1178
 
1067
1179
  // tools/files/readFileTool.ts
1068
- import { tool as tool6 } from "@openai/agents";
1180
+ import { tool as tool7 } from "@openai/agents";
1069
1181
  import { readFile } from "fs/promises";
1070
- import { z as z6 } from "zod";
1071
- var ToolParameters5 = z6.object({
1072
- fileName: z6.string().describe("The name of the file to read.")
1182
+ import { z as z7 } from "zod";
1183
+ var ToolParameters6 = z7.object({
1184
+ fileName: z7.string().describe("The name of the file to read.")
1073
1185
  });
1074
- var readFileTool = tool6({
1186
+ var readFileTool = tool7({
1075
1187
  name: "readFile",
1076
1188
  description: "Reads the contents of a specified file.",
1077
- parameters: ToolParameters5,
1189
+ parameters: ToolParameters6,
1078
1190
  async execute({ fileName }) {
1079
1191
  try {
1192
+ const normalized = String(fileName).replace(/\\/g, "/");
1193
+ const m = normalized.match(/(?:^|\/)skills\/([A-Za-z0-9_-]+)\.md(?:$|[?#])/);
1194
+ if (m && m[1]) {
1195
+ trackedState.session?.addSkillRead?.(m[1]);
1196
+ }
1080
1197
  const resolvedPath = resolvePath(trackedState.cwd, fileName);
1081
1198
  return readFile(resolvedPath, "utf8");
1082
1199
  } catch (error) {
@@ -1086,28 +1203,28 @@ var readFileTool = tool6({
1086
1203
  });
1087
1204
 
1088
1205
  // tools/general/codeInterpreterTool.ts
1089
- import { tool as tool7 } from "@openai/agents";
1206
+ import { tool as tool8 } from "@openai/agents";
1090
1207
  import chalk8 from "chalk";
1091
1208
  import { exec } from "child_process";
1092
1209
  import { unlink, writeFile as writeFile2 } from "fs/promises";
1093
- import path5 from "path";
1210
+ import path6 from "path";
1094
1211
  import { promisify as promisify3 } from "util";
1095
- import { z as z7 } from "zod";
1212
+ import { z as z8 } from "zod";
1096
1213
  var execAsync = promisify3(exec);
1097
- var CodeInterpreterParameters = z7.object({
1098
- code: z7.string().describe("The code to execute."),
1099
- language: z7.enum(["python", "javascript"]).optional().default("python").describe(
1214
+ var CodeInterpreterParameters = z8.object({
1215
+ code: z8.string().describe("The code to execute."),
1216
+ language: z8.enum(["python", "javascript"]).optional().default("python").describe(
1100
1217
  "The programming language of the code."
1101
1218
  )
1102
1219
  });
1103
- var codeInterpreterTool = tool7({
1220
+ var codeInterpreterTool = tool8({
1104
1221
  name: "code_interpreter",
1105
1222
  description: "Executes Python or JavaScript code in a local environment. This is useful for data analysis, complex calculations, and more. All code will be executed in the current workspace.",
1106
1223
  parameters: CodeInterpreterParameters,
1107
- execute: execute2,
1108
- needsApproval
1224
+ execute: execute3,
1225
+ needsApproval: needsApproval2
1109
1226
  });
1110
- async function needsApproval(runContext, { code, language }, callId) {
1227
+ async function needsApproval2(runContext, { code, language }, callId) {
1111
1228
  if (callId && runContext.isToolApproved({ toolName: "code_interpreter", callId })) {
1112
1229
  return false;
1113
1230
  }
@@ -1126,10 +1243,10 @@ ${chalk8.bold.bgYellow.black(` Code interpreter (${language}) approval required:
1126
1243
  }
1127
1244
  return !autoApproved;
1128
1245
  }
1129
- async function execute2({ code, language }) {
1246
+ async function execute3({ code, language }) {
1130
1247
  const extension = language === "javascript" ? "js" : "py";
1131
1248
  const interpreter = language === "javascript" ? "node" : "python3";
1132
- const tempFile = path5.join(trackedState.cwd, `.temp_code_${Date.now()}.${extension}`);
1249
+ const tempFile = path6.join(trackedState.cwd, `.temp_code_${Date.now()}.${extension}`);
1133
1250
  try {
1134
1251
  await writeFile2(tempFile, code, "utf8");
1135
1252
  const { stdout, stderr } = await execAsync(`${interpreter} ${tempFile}`);
@@ -1148,16 +1265,16 @@ STDERR: ${error.stderr || ""}`;
1148
1265
  }
1149
1266
 
1150
1267
  // tools/general/setInterpreterAutoApproveTool.ts
1151
- import { tool as tool8 } from "@openai/agents";
1152
- import { z as z8 } from "zod";
1268
+ import { tool as tool9 } from "@openai/agents";
1269
+ import { z as z9 } from "zod";
1153
1270
 
1154
1271
  // utils/files/updateEnv.ts
1155
1272
  import { existsSync as existsSync6 } from "fs";
1156
1273
  import { readFile as readFile2, writeFile as writeFile3 } from "fs/promises";
1157
- import { join as join6 } from "path";
1274
+ import { join as join7 } from "path";
1158
1275
  async function updateEnv(key, value) {
1159
1276
  process.env[key] = value;
1160
- const envPath = join6(trackedState.cwd, ".env");
1277
+ const envPath = join7(trackedState.cwd, ".env");
1161
1278
  let envContent = "";
1162
1279
  if (existsSync6(envPath)) {
1163
1280
  envContent = await readFile2(envPath, "utf8");
@@ -1176,10 +1293,10 @@ async function updateEnv(key, value) {
1176
1293
  }
1177
1294
 
1178
1295
  // tools/general/setInterpreterAutoApproveTool.ts
1179
- var SetInterpreterAutoApproveParameters = z8.object({
1180
- autoApprove: z8.boolean()
1296
+ var SetInterpreterAutoApproveParameters = z9.object({
1297
+ autoApprove: z9.boolean()
1181
1298
  });
1182
- var setInterpreterAutoApproveTool = tool8({
1299
+ var setInterpreterAutoApproveTool = tool9({
1183
1300
  name: "setInterpreterAutoApproveTool",
1184
1301
  description: "Enable or disable automatic approval for code interpreter by setting HARPER_AGENT_AUTO_APPROVE_CODE_INTERPRETER=1 or 0 in .env and current process.",
1185
1302
  parameters: SetInterpreterAutoApproveParameters,
@@ -1202,12 +1319,12 @@ var setInterpreterAutoApproveTool = tool8({
1202
1319
  });
1203
1320
 
1204
1321
  // tools/general/setPatchAutoApproveTool.ts
1205
- import { tool as tool9 } from "@openai/agents";
1206
- import { z as z9 } from "zod";
1207
- var SetPatchAutoApproveParameters = z9.object({
1208
- autoApprove: z9.boolean()
1322
+ import { tool as tool10 } from "@openai/agents";
1323
+ import { z as z10 } from "zod";
1324
+ var SetPatchAutoApproveParameters = z10.object({
1325
+ autoApprove: z10.boolean()
1209
1326
  });
1210
- var setPatchAutoApproveTool = tool9({
1327
+ var setPatchAutoApproveTool = tool10({
1211
1328
  name: "setPatchAutoApproveTool",
1212
1329
  description: "Enable or disable automatic approval for patch commands by setting HARPER_AGENT_AUTO_APPROVE_PATCHES=1 or 0 in .env and current process.",
1213
1330
  parameters: SetPatchAutoApproveParameters,
@@ -1230,12 +1347,12 @@ var setPatchAutoApproveTool = tool9({
1230
1347
  });
1231
1348
 
1232
1349
  // tools/general/setShellAutoApproveTool.ts
1233
- import { tool as tool10 } from "@openai/agents";
1234
- import { z as z10 } from "zod";
1235
- var SetShellAutoApproveParameters = z10.object({
1236
- autoApprove: z10.boolean()
1350
+ import { tool as tool11 } from "@openai/agents";
1351
+ import { z as z11 } from "zod";
1352
+ var SetShellAutoApproveParameters = z11.object({
1353
+ autoApprove: z11.boolean()
1237
1354
  });
1238
- var setShellAutoApproveTool = tool10({
1355
+ var setShellAutoApproveTool = tool11({
1239
1356
  name: "setShellAutoApproveTool",
1240
1357
  description: "Enable or disable automatic approval for shell commands by setting HARPER_AGENT_AUTO_APPROVE_SHELL=1 or 0 in .env and current process.",
1241
1358
  parameters: SetShellAutoApproveParameters,
@@ -1258,9 +1375,9 @@ var setShellAutoApproveTool = tool10({
1258
1375
  });
1259
1376
 
1260
1377
  // tools/general/shellTool.ts
1261
- import { tool as tool11 } from "@openai/agents";
1378
+ import { tool as tool12 } from "@openai/agents";
1262
1379
  import chalk9 from "chalk";
1263
- import { z as z11 } from "zod";
1380
+ import { z as z12 } from "zod";
1264
1381
 
1265
1382
  // utils/files/mentionsIgnoredPath.ts
1266
1383
  function mentionsIgnoredPath(command) {
@@ -1372,11 +1489,11 @@ var LocalShell = class {
1372
1489
  };
1373
1490
 
1374
1491
  // tools/general/shellTool.ts
1375
- var ShellParameters = z11.object({
1376
- commands: z11.array(z11.string()).describe("The commands to execute.")
1492
+ var ShellParameters = z12.object({
1493
+ commands: z12.array(z12.string()).describe("The commands to execute.")
1377
1494
  });
1378
1495
  var shell = new LocalShell();
1379
- var shellTool = tool11({
1496
+ var shellTool = tool12({
1380
1497
  name: "shellToolForCommandsWithoutABetterTool",
1381
1498
  description: "Executes shell commands.",
1382
1499
  parameters: ShellParameters,
@@ -1433,15 +1550,15 @@ TIMEOUT`;
1433
1550
  });
1434
1551
 
1435
1552
  // tools/git/gitAddTool.ts
1436
- import { tool as tool12 } from "@openai/agents";
1553
+ import { tool as tool13 } from "@openai/agents";
1437
1554
  import { execFile as execFile3 } from "child_process";
1438
1555
  import { promisify as promisify5 } from "util";
1439
- import { z as z12 } from "zod";
1556
+ import { z as z13 } from "zod";
1440
1557
  var execFileAsync3 = promisify5(execFile3);
1441
- var GitAddParameters = z12.object({
1442
- files: z12.array(z12.string()).describe("The files to add. If not provided, all changes will be added.")
1558
+ var GitAddParameters = z13.object({
1559
+ files: z13.array(z13.string()).describe("The files to add. If not provided, all changes will be added.")
1443
1560
  });
1444
- var gitAddTool = tool12({
1561
+ var gitAddTool = tool13({
1445
1562
  name: "gitAddTool",
1446
1563
  description: "Add file contents to the index.",
1447
1564
  parameters: GitAddParameters,
@@ -1462,16 +1579,16 @@ var gitAddTool = tool12({
1462
1579
  });
1463
1580
 
1464
1581
  // tools/git/gitBranchTool.ts
1465
- import { tool as tool13 } from "@openai/agents";
1582
+ import { tool as tool14 } from "@openai/agents";
1466
1583
  import { execFile as execFile4 } from "child_process";
1467
1584
  import { promisify as promisify6 } from "util";
1468
- import { z as z13 } from "zod";
1585
+ import { z as z14 } from "zod";
1469
1586
  var execFileAsync4 = promisify6(execFile4);
1470
- var GitBranchParameters = z13.object({
1471
- branchName: z13.string().describe("The name of the branch to create or switch to."),
1472
- create: z13.boolean().optional().default(false).describe("Whether to create a new branch.")
1587
+ var GitBranchParameters = z14.object({
1588
+ branchName: z14.string().describe("The name of the branch to create or switch to."),
1589
+ create: z14.boolean().optional().default(false).describe("Whether to create a new branch.")
1473
1590
  });
1474
- var gitBranchTool = tool13({
1591
+ var gitBranchTool = tool14({
1475
1592
  name: "gitBranchTool",
1476
1593
  description: "Create or switch to a git branch.",
1477
1594
  parameters: GitBranchParameters,
@@ -1488,18 +1605,18 @@ var gitBranchTool = tool13({
1488
1605
  });
1489
1606
 
1490
1607
  // tools/git/gitCommitTool.ts
1491
- import { tool as tool14 } from "@openai/agents";
1608
+ import { tool as tool15 } from "@openai/agents";
1492
1609
  import { execFile as execFile5 } from "child_process";
1493
1610
  import { promisify as promisify7 } from "util";
1494
- import { z as z14 } from "zod";
1611
+ import { z as z15 } from "zod";
1495
1612
  var execFileAsync5 = promisify7(execFile5);
1496
- var GitCommitParameters = z14.object({
1497
- message: z14.string().describe("The commit message."),
1498
- addAll: z14.boolean().optional().default(false).describe(
1613
+ var GitCommitParameters = z15.object({
1614
+ message: z15.string().describe("The commit message."),
1615
+ addAll: z15.boolean().optional().default(false).describe(
1499
1616
  "Whether to add all changes before committing (git commit -am)."
1500
1617
  )
1501
1618
  });
1502
- var gitCommitTool = tool14({
1619
+ var gitCommitTool = tool15({
1503
1620
  name: "gitCommitTool",
1504
1621
  description: "Commit changes to the repository.",
1505
1622
  parameters: GitCommitParameters,
@@ -1515,16 +1632,16 @@ var gitCommitTool = tool14({
1515
1632
  });
1516
1633
 
1517
1634
  // tools/git/gitLogTool.ts
1518
- import { tool as tool15 } from "@openai/agents";
1635
+ import { tool as tool16 } from "@openai/agents";
1519
1636
  import { execFile as execFile6 } from "child_process";
1520
1637
  import { promisify as promisify8 } from "util";
1521
- import { z as z15 } from "zod";
1638
+ import { z as z16 } from "zod";
1522
1639
  var execFileAsync6 = promisify8(execFile6);
1523
- var GitLogParameters = z15.object({
1524
- count: z15.number().optional().default(10).describe("Number of commits to show."),
1525
- oneline: z15.boolean().optional().default(true).describe("Whether to show log in oneline format.")
1640
+ var GitLogParameters = z16.object({
1641
+ count: z16.number().optional().default(10).describe("Number of commits to show."),
1642
+ oneline: z16.boolean().optional().default(true).describe("Whether to show log in oneline format.")
1526
1643
  });
1527
- var gitLogTool = tool15({
1644
+ var gitLogTool = tool16({
1528
1645
  name: "gitLogTool",
1529
1646
  description: "Show commit logs.",
1530
1647
  parameters: GitLogParameters,
@@ -1543,17 +1660,17 @@ var gitLogTool = tool15({
1543
1660
  });
1544
1661
 
1545
1662
  // tools/git/gitStashTool.ts
1546
- import { tool as tool16 } from "@openai/agents";
1663
+ import { tool as tool17 } from "@openai/agents";
1547
1664
  import { execFile as execFile7 } from "child_process";
1548
1665
  import { promisify as promisify9 } from "util";
1549
- import { z as z16 } from "zod";
1666
+ import { z as z17 } from "zod";
1550
1667
  var execFileAsync7 = promisify9(execFile7);
1551
1668
  var allowedActions = ["push", "pop", "apply", "list"];
1552
- var GitStashParameters = z16.object({
1553
- action: z16.string().describe("The stash action to perform: " + allowedActions.join(", ")),
1554
- message: z16.string().describe("A message for the stash change.")
1669
+ var GitStashParameters = z17.object({
1670
+ action: z17.string().describe("The stash action to perform: " + allowedActions.join(", ")),
1671
+ message: z17.string().describe("A message for the stash change.")
1555
1672
  });
1556
- var gitStashTool = tool16({
1673
+ var gitStashTool = tool17({
1557
1674
  name: "gitStashTool",
1558
1675
  description: "Stash changes or apply a stash.",
1559
1676
  parameters: GitStashParameters,
@@ -1575,15 +1692,15 @@ var gitStashTool = tool16({
1575
1692
  });
1576
1693
 
1577
1694
  // tools/git/gitStatusTool.ts
1578
- import { tool as tool17 } from "@openai/agents";
1695
+ import { tool as tool18 } from "@openai/agents";
1579
1696
  import { execFile as execFile8 } from "child_process";
1580
1697
  import { promisify as promisify10 } from "util";
1581
- import { z as z17 } from "zod";
1698
+ import { z as z18 } from "zod";
1582
1699
  var execFileAsync8 = promisify10(execFile8);
1583
- var GitStatusParameters = z17.object({
1584
- short: z17.boolean().optional().default(false).describe("Whether to show the status in short format.")
1700
+ var GitStatusParameters = z18.object({
1701
+ short: z18.boolean().optional().default(false).describe("Whether to show the status in short format.")
1585
1702
  });
1586
- var gitStatusTool = tool17({
1703
+ var gitStatusTool = tool18({
1587
1704
  name: "gitStatusTool",
1588
1705
  description: "Show the working tree status.",
1589
1706
  parameters: GitStatusParameters,
@@ -1602,17 +1719,17 @@ var gitStatusTool = tool17({
1602
1719
  });
1603
1720
 
1604
1721
  // tools/git/gitWorkspaceTool.ts
1605
- import { tool as tool18 } from "@openai/agents";
1722
+ import { tool as tool19 } from "@openai/agents";
1606
1723
  import { execFile as execFile9 } from "child_process";
1607
1724
  import { promisify as promisify11 } from "util";
1608
- import { z as z18 } from "zod";
1725
+ import { z as z19 } from "zod";
1609
1726
  var execFileAsync9 = promisify11(execFile9);
1610
- var GitWorkspaceParameters = z18.object({
1611
- path: z18.string().describe("The path where the new workspace (worktree) should be created."),
1612
- branchName: z18.string().describe("The name of the branch to use in the new workspace."),
1613
- createBranch: z18.boolean().optional().default(false).describe("Whether to create a new branch for this workspace.")
1727
+ var GitWorkspaceParameters = z19.object({
1728
+ path: z19.string().describe("The path where the new workspace (worktree) should be created."),
1729
+ branchName: z19.string().describe("The name of the branch to use in the new workspace."),
1730
+ createBranch: z19.boolean().optional().default(false).describe("Whether to create a new branch for this workspace.")
1614
1731
  });
1615
- var gitWorkspaceTool = tool18({
1732
+ var gitWorkspaceTool = tool19({
1616
1733
  name: "gitWorkspaceTool",
1617
1734
  description: "Create a new workspace (git worktree) for parallel work.",
1618
1735
  parameters: GitWorkspaceParameters,
@@ -1629,13 +1746,13 @@ var gitWorkspaceTool = tool18({
1629
1746
  });
1630
1747
 
1631
1748
  // tools/harper/checkHarperStatusTool.ts
1632
- import { tool as tool19 } from "@openai/agents";
1633
- import { z as z19 } from "zod";
1634
- var ToolParameters6 = z19.object({});
1635
- var checkHarperStatusTool = tool19({
1749
+ import { tool as tool20 } from "@openai/agents";
1750
+ import { z as z20 } from "zod";
1751
+ var ToolParameters7 = z20.object({});
1752
+ var checkHarperStatusTool = tool20({
1636
1753
  name: "checkHarperStatusTool",
1637
1754
  description: "Checks if a Harper application is currently running.",
1638
- parameters: ToolParameters6,
1755
+ parameters: ToolParameters7,
1639
1756
  async execute() {
1640
1757
  if (harperProcess.running) {
1641
1758
  return "A Harper application is currently running.";
@@ -1646,10 +1763,10 @@ var checkHarperStatusTool = tool19({
1646
1763
  });
1647
1764
 
1648
1765
  // tools/harper/createNewHarperApplicationTool.ts
1649
- import { tool as tool20 } from "@openai/agents";
1766
+ import { tool as tool21 } from "@openai/agents";
1650
1767
  import { execSync as execSync3 } from "child_process";
1651
- import path6 from "path";
1652
- import { z as z20 } from "zod";
1768
+ import path7 from "path";
1769
+ import { z as z21 } from "zod";
1653
1770
 
1654
1771
  // utils/package/buildHarperCreateCommand.ts
1655
1772
  function buildCreateCommand(pm, appName, template) {
@@ -1699,16 +1816,16 @@ var PM_DISPLAY = {
1699
1816
  };
1700
1817
 
1701
1818
  // tools/harper/createNewHarperApplicationTool.ts
1702
- var ToolParameters7 = z20.object({
1703
- directoryName: z20.string().describe("The name of the directory to create the application in."),
1704
- template: z20.enum(["vanilla-ts", "vanilla", "react-ts", "react"]).optional().describe("The template to use for the new application. Defaults to vanilla-ts.").default("vanilla-ts")
1819
+ var ToolParameters8 = z21.object({
1820
+ directoryName: z21.string().describe("The name of the directory to create the application in."),
1821
+ template: z21.enum(["vanilla-ts", "vanilla", "react-ts", "react"]).optional().describe("The template to use for the new application. Defaults to vanilla-ts.").default("vanilla-ts")
1705
1822
  });
1706
- async function execute3({ directoryName, template }) {
1823
+ async function execute4({ directoryName, template }) {
1707
1824
  const currentCwd = trackedState.cwd;
1708
1825
  const resolvedPath = resolvePath(currentCwd, directoryName);
1709
1826
  const isCurrentDir = resolvedPath === currentCwd;
1710
- const executionCwd = isCurrentDir ? resolvedPath : path6.dirname(resolvedPath);
1711
- const appName = isCurrentDir ? "." : path6.basename(resolvedPath);
1827
+ const executionCwd = isCurrentDir ? resolvedPath : path7.dirname(resolvedPath);
1828
+ const appName = isCurrentDir ? "." : path7.basename(resolvedPath);
1712
1829
  try {
1713
1830
  console.log(`Creating new Harper application in ${resolvedPath} using template ${template}...`);
1714
1831
  const pm = pickPreferredPackageManager();
@@ -1720,7 +1837,7 @@ async function execute3({ directoryName, template }) {
1720
1837
  });
1721
1838
  console.log(`Initializing new Git repository in ${resolvedPath}...`);
1722
1839
  execSync3("git init", { cwd: resolvedPath, stdio: "ignore" });
1723
- const switchedDir = await execute({ path: resolvedPath });
1840
+ const switchedDir = await execute2({ path: resolvedPath });
1724
1841
  return `Successfully created a new Harper application in '${resolvedPath}' using template '${template}' with a matching Git repository initialized. Use the readDir and readFile tools to inspect the contents of the application. ${switchedDir}.`;
1725
1842
  } catch (error) {
1726
1843
  let errorMsg = `Error creating new Harper application: ${error.message}`;
@@ -1733,33 +1850,33 @@ ${error.stdout}`;
1733
1850
  return errorMsg;
1734
1851
  }
1735
1852
  }
1736
- var createNewHarperApplicationTool = tool20({
1853
+ var createNewHarperApplicationTool = tool21({
1737
1854
  name: "createNewHarperApplicationTool",
1738
1855
  description: "Creates a new Harper application using the best available package manager (yarn/pnpm/bun/deno, falling back to npm).",
1739
- parameters: ToolParameters7,
1740
- execute: execute3
1856
+ parameters: ToolParameters8,
1857
+ execute: execute4
1741
1858
  });
1742
1859
 
1743
1860
  // tools/harper/getHarperConfigSchemaTool.ts
1744
- import { tool as tool21 } from "@openai/agents";
1861
+ import { tool as tool22 } from "@openai/agents";
1745
1862
  import { readFile as readFile3 } from "fs/promises";
1746
- import { createRequire } from "module";
1747
- import { dirname, join as join7 } from "path";
1748
- import { z as z21 } from "zod";
1749
- var ToolParameters8 = z21.object({
1750
- schemaType: z21.enum(["app", "root"]).describe(
1863
+ import { createRequire as createRequire2 } from "module";
1864
+ import { dirname as dirname2, join as join8 } from "path";
1865
+ import { z as z22 } from "zod";
1866
+ var ToolParameters9 = z22.object({
1867
+ schemaType: z22.enum(["app", "root"]).describe(
1751
1868
  'The type of configuration schema to retrieve: "app" for application configuration or "root" for root Harper configuration.'
1752
1869
  )
1753
1870
  });
1754
- var getHarperConfigSchemaTool = tool21({
1871
+ var getHarperConfigSchemaTool = tool22({
1755
1872
  name: "getHarperConfigSchemaTool",
1756
1873
  description: "Returns the JSON schema for HarperDB configuration files (either app or root), which describes the config.yaml or harperdb-config.yaml files.",
1757
- parameters: ToolParameters8,
1874
+ parameters: ToolParameters9,
1758
1875
  async execute({ schemaType }) {
1759
1876
  try {
1760
1877
  return await readFile3(
1761
- join7(
1762
- dirname(createRequire(import.meta.url).resolve("harperdb")),
1878
+ join8(
1879
+ dirname2(createRequire2(import.meta.url).resolve("harperdb")),
1763
1880
  `config-${schemaType}.schema.json`
1764
1881
  ),
1765
1882
  "utf8"
@@ -1771,13 +1888,13 @@ var getHarperConfigSchemaTool = tool21({
1771
1888
  });
1772
1889
 
1773
1890
  // tools/harper/getHarperResourceInterfaceTool.ts
1774
- import { tool as tool22 } from "@openai/agents";
1891
+ import { tool as tool23 } from "@openai/agents";
1775
1892
  import { readFile as readFile4 } from "fs/promises";
1776
- import { createRequire as createRequire2 } from "module";
1777
- import { dirname as dirname2, join as join8 } from "path";
1778
- import { z as z22 } from "zod";
1779
- var ToolParameters9 = z22.object({
1780
- resourceFile: z22.enum([
1893
+ import { createRequire as createRequire3 } from "module";
1894
+ import { dirname as dirname3, join as join9 } from "path";
1895
+ import { z as z23 } from "zod";
1896
+ var ToolParameters10 = z23.object({
1897
+ resourceFile: z23.enum([
1781
1898
  "ResourceInterfaceV2",
1782
1899
  "ResourceInterface",
1783
1900
  "Table",
@@ -1787,15 +1904,15 @@ var ToolParameters9 = z22.object({
1787
1904
  "The resource-related definition file to read. Defaults to ResourceInterfaceV2."
1788
1905
  ).default("ResourceInterfaceV2")
1789
1906
  });
1790
- var getHarperResourceInterfaceTool = tool22({
1907
+ var getHarperResourceInterfaceTool = tool23({
1791
1908
  name: "getHarperResourceInterfaceTool",
1792
1909
  description: "Reads HarperDB resource interface and class definitions (like ResourceInterfaceV2.d.ts) to understand how resources and tables are structured.",
1793
- parameters: ToolParameters9,
1910
+ parameters: ToolParameters10,
1794
1911
  async execute({ resourceFile }) {
1795
1912
  try {
1796
1913
  return await readFile4(
1797
- join8(
1798
- dirname2(createRequire2(import.meta.url).resolve("harperdb")),
1914
+ join9(
1915
+ dirname3(createRequire3(import.meta.url).resolve("harperdb")),
1799
1916
  "resources",
1800
1917
  `${resourceFile}.d.ts`
1801
1918
  ),
@@ -1808,21 +1925,21 @@ var getHarperResourceInterfaceTool = tool22({
1808
1925
  });
1809
1926
 
1810
1927
  // tools/harper/getHarperSchemaGraphQLTool.ts
1811
- import { tool as tool23 } from "@openai/agents";
1928
+ import { tool as tool24 } from "@openai/agents";
1812
1929
  import { readFile as readFile5 } from "fs/promises";
1813
- import { createRequire as createRequire3 } from "module";
1814
- import { dirname as dirname3, join as join9 } from "path";
1815
- import { z as z23 } from "zod";
1816
- var ToolParameters10 = z23.object({});
1817
- var getHarperSchemaGraphQLTool = tool23({
1930
+ import { createRequire as createRequire4 } from "module";
1931
+ import { dirname as dirname4, join as join10 } from "path";
1932
+ import { z as z24 } from "zod";
1933
+ var ToolParameters11 = z24.object({});
1934
+ var getHarperSchemaGraphQLTool = tool24({
1818
1935
  name: "getHarperSchemaGraphQLTool",
1819
1936
  description: "Returns the GraphQL schema for HarperDB schema files, which define the structure of HarperDB database tables.",
1820
- parameters: ToolParameters10,
1937
+ parameters: ToolParameters11,
1821
1938
  async execute() {
1822
1939
  try {
1823
1940
  return await readFile5(
1824
- join9(
1825
- dirname3(createRequire3(import.meta.url).resolve("harperdb")),
1941
+ join10(
1942
+ dirname4(createRequire4(import.meta.url).resolve("harperdb")),
1826
1943
  `schema.graphql`
1827
1944
  ),
1828
1945
  "utf8"
@@ -1833,61 +1950,6 @@ var getHarperSchemaGraphQLTool = tool23({
1833
1950
  }
1834
1951
  });
1835
1952
 
1836
- // tools/harper/getHarperSkillTool.ts
1837
- import { tool as tool24 } from "@openai/agents";
1838
- import { readdirSync, readFileSync as readFileSync5 } from "fs";
1839
- import { createRequire as createRequire4 } from "module";
1840
- import { dirname as dirname4, join as join10 } from "path";
1841
- import { z as z24 } from "zod";
1842
- var createHarper = dirname4(createRequire4(import.meta.url).resolve("create-harper"));
1843
- var agentsMarkdown = join10(
1844
- createHarper,
1845
- "AGENTS.md"
1846
- );
1847
- var skillsDir = join10(
1848
- createHarper,
1849
- "template-vanilla",
1850
- "skills"
1851
- );
1852
- var skillLinkRegex = /\[[^\]]+]\(skills\/([^)]+)\.md\)/g;
1853
- var skills = getSkills();
1854
- var ToolParameters11 = z24.object({
1855
- skill: z24.enum(skills.length > 0 ? skills : ["none"]).describe(
1856
- "The name of the skill to retrieve."
1857
- )
1858
- });
1859
- var getHarperSkillTool = tool24({
1860
- name: "getHarperSkill",
1861
- description: getSkillsDescription(),
1862
- parameters: ToolParameters11,
1863
- execute: execute4
1864
- });
1865
- function getSkillsDescription() {
1866
- try {
1867
- return readFileSync5(agentsMarkdown, "utf8").replace("This repository contains", "This tool describes").replace(skillLinkRegex, "$1");
1868
- } catch {
1869
- return "Returns the contents of a Harper skill markdown file. Skills provide guidance on developing Harper applications.";
1870
- }
1871
- }
1872
- function getSkills() {
1873
- try {
1874
- return readdirSync(skillsDir).filter((file) => file.endsWith(".md")).map((file) => file.replace(".md", ""));
1875
- } catch {
1876
- return [];
1877
- }
1878
- }
1879
- async function execute4({ skill }) {
1880
- if (skill === "none") {
1881
- return "No skills found.";
1882
- }
1883
- try {
1884
- const filePath = join10(skillsDir, `${skill}.md`);
1885
- return readFileSync5(filePath, "utf8");
1886
- } catch (error) {
1887
- return `Error reading Harper skill "${skill}": ${error}`;
1888
- }
1889
- }
1890
-
1891
1953
  // tools/harper/hitHarperAPITool.ts
1892
1954
  import { tool as tool25 } from "@openai/agents";
1893
1955
  import { z as z25 } from "zod";
@@ -1917,14 +1979,14 @@ var hitHarperAPITool = tool25({
1917
1979
  }
1918
1980
  return false;
1919
1981
  },
1920
- async execute({ path: path7 = "/openapi", port, method = "GET", body }) {
1982
+ async execute({ path: path8 = "/openapi", port, method = "GET", body }) {
1921
1983
  try {
1922
1984
  const effectivePort = port ?? (harperProcess.running ? harperProcess.httpPort : void 0);
1923
1985
  if (!effectivePort) {
1924
1986
  return `Error: No Harper application is currently running and no port was specified.`;
1925
1987
  }
1926
1988
  const response = await fetch(
1927
- `http://localhost:${effectivePort}${path7.startsWith("/") ? "" : "/"}${path7}`,
1989
+ `http://localhost:${effectivePort}${path8.startsWith("/") ? "" : "/"}${path8}`,
1928
1990
  {
1929
1991
  method,
1930
1992
  headers: body ? { "Content-Type": "application/json" } : {},
@@ -2222,7 +2284,7 @@ var compactionModelSettings = {
2222
2284
  };
2223
2285
 
2224
2286
  // utils/shell/askQuestion.ts
2225
- import { createInterface } from "readline/promises";
2287
+ import { createInterface } from "readline";
2226
2288
 
2227
2289
  // lifecycle/handleExit.ts
2228
2290
  async function handleExit() {
@@ -2234,16 +2296,39 @@ async function handleExit() {
2234
2296
  async function askQuestion(query) {
2235
2297
  const rl = createInterface({
2236
2298
  input: process.stdin,
2237
- output: process.stdout
2299
+ output: process.stdout,
2300
+ terminal: true
2238
2301
  });
2239
2302
  rl.on("SIGINT", handleExit);
2240
- try {
2241
- const response = await rl.question(query);
2242
- console.log("");
2243
- return response;
2244
- } finally {
2245
- rl.close();
2246
- }
2303
+ return await new Promise((resolve2) => {
2304
+ const lines = [];
2305
+ let timer = null;
2306
+ let finished = false;
2307
+ const DEBOUNCE_MS = 75;
2308
+ const finish = () => {
2309
+ if (finished) {
2310
+ return;
2311
+ }
2312
+ finished = true;
2313
+ if (timer) {
2314
+ clearTimeout(timer);
2315
+ }
2316
+ rl.removeListener("line", onLine);
2317
+ console.log("");
2318
+ rl.close();
2319
+ resolve2(lines.join("\n"));
2320
+ };
2321
+ const onLine = (line) => {
2322
+ lines.push(line);
2323
+ if (timer) {
2324
+ clearTimeout(timer);
2325
+ }
2326
+ timer = setTimeout(finish, DEBOUNCE_MS);
2327
+ };
2328
+ rl.on("line", onLine);
2329
+ rl.setPrompt(query);
2330
+ rl.prompt();
2331
+ });
2247
2332
  }
2248
2333
 
2249
2334
  // utils/shell/ensureApiKey.ts
@@ -2475,12 +2560,15 @@ var DiskSession = class extends MemorySession {
2475
2560
  if (existsSync8(this.filePath)) {
2476
2561
  try {
2477
2562
  const data = await readFile6(this.filePath, "utf-8");
2478
- return JSON.parse(data);
2563
+ const parsed = JSON.parse(data);
2564
+ parsed.sessions = parsed.sessions || {};
2565
+ parsed.skillsRead = parsed.skillsRead || {};
2566
+ return parsed;
2479
2567
  } catch (e) {
2480
2568
  console.error(`Failed to read session file ${this.filePath}:`, e);
2481
2569
  }
2482
2570
  }
2483
- return { sessions: {} };
2571
+ return { sessions: {}, skillsRead: {} };
2484
2572
  }
2485
2573
  async updateStorage(update) {
2486
2574
  const storage = await this.loadStorage();
@@ -2532,8 +2620,30 @@ var DiskSession = class extends MemorySession {
2532
2620
  const sessionId = await this.getSessionId();
2533
2621
  await this.updateStorage((storage) => {
2534
2622
  delete storage.sessions[sessionId];
2623
+ if (storage.skillsRead) {
2624
+ delete storage.skillsRead[sessionId];
2625
+ }
2626
+ });
2627
+ }
2628
+ async addSkillRead(skill) {
2629
+ await this.ready;
2630
+ const sessionId = await this.getSessionId();
2631
+ await this.updateStorage((storage) => {
2632
+ if (!storage.skillsRead) {
2633
+ storage.skillsRead = {};
2634
+ }
2635
+ const arr = storage.skillsRead[sessionId] ?? (storage.skillsRead[sessionId] = []);
2636
+ if (!arr.includes(skill)) {
2637
+ arr.push(skill);
2638
+ }
2535
2639
  });
2536
2640
  }
2641
+ async getSkillsRead() {
2642
+ await this.ready;
2643
+ const sessionId = await this.getSessionId();
2644
+ const storage = await this.loadStorage();
2645
+ return storage.skillsRead?.[sessionId] ?? [];
2646
+ }
2537
2647
  };
2538
2648
 
2539
2649
  // utils/sessions/MemoryCompactionSession.ts
@@ -2542,19 +2652,18 @@ import {
2542
2652
  } from "@openai/agents";
2543
2653
 
2544
2654
  // utils/sessions/compactConversation.ts
2545
- import { Agent as Agent2, run, system } from "@openai/agents";
2655
+ import { Agent, run, system } from "@openai/agents";
2546
2656
  async function compactConversation(items) {
2547
- const firstItem = items[0];
2548
2657
  const recentItems = items.slice(-3);
2549
- const itemsToCompact = items.slice(1, -3);
2658
+ const itemsToCompact = items.slice(0, -3);
2550
2659
  let noticeContent = "... conversation history compacted ...";
2551
2660
  if (trackedState.compactionModel && itemsToCompact.length > 0) {
2552
2661
  try {
2553
- const agent = new Agent2({
2662
+ const agent = new Agent({
2554
2663
  name: "History Compactor",
2555
2664
  model: isOpenAIModel(trackedState.compactionModel) ? trackedState.compactionModel : getModel(trackedState.compactionModel),
2556
2665
  modelSettings: compactionModelSettings,
2557
- instructions: "Summarize the conversation history so far into a single concise paragraph. Focus on the key facts and decisions made."
2666
+ instructions: "Compact the provided conversation history into key observations. Focus on what seems likely to be needed later. Be concise and avoid repeating information."
2558
2667
  });
2559
2668
  const result = await run(
2560
2669
  agent,
@@ -2563,7 +2672,8 @@ async function compactConversation(items) {
2563
2672
  const summary = result.finalOutput;
2564
2673
  if (summary && summary.trim().length > 0) {
2565
2674
  const s = summary.replace(/\s+/g, " ").trim();
2566
- noticeContent = `... conversation history compacted: ${s} ...`;
2675
+ noticeContent = `Key observations from earlier:
2676
+ ${s}`;
2567
2677
  }
2568
2678
  } catch (err) {
2569
2679
  const msg = String(err?.message || err || "");
@@ -2573,7 +2683,7 @@ async function compactConversation(items) {
2573
2683
  }
2574
2684
  }
2575
2685
  }
2576
- const itemsToAdd = [firstItem, system(noticeContent), ...recentItems].filter(excludeFalsy);
2686
+ const itemsToAdd = [system(noticeContent), ...recentItems].filter(excludeFalsy);
2577
2687
  return { noticeContent, itemsToAdd };
2578
2688
  }
2579
2689
 
@@ -2615,6 +2725,7 @@ var MemoryCompactionSession = class {
2615
2725
  underlyingSession;
2616
2726
  triggerTokens;
2617
2727
  itemsAddedSinceLastCompaction = 0;
2728
+ skillsReadLocal = /* @__PURE__ */ new Set();
2618
2729
  constructor(options) {
2619
2730
  this.underlyingSession = options.underlyingSession ?? new MemorySession2();
2620
2731
  if (trackedState.compactionModel) {
@@ -2625,6 +2736,25 @@ var MemoryCompactionSession = class {
2625
2736
  async getSessionId() {
2626
2737
  return this.underlyingSession.getSessionId();
2627
2738
  }
2739
+ async addSkillRead(skill) {
2740
+ const u = this.underlyingSession;
2741
+ if (u && typeof u.addSkillRead === "function") {
2742
+ return u.addSkillRead(skill);
2743
+ }
2744
+ this.skillsReadLocal.add(skill);
2745
+ }
2746
+ async getSkillsRead() {
2747
+ const u = this.underlyingSession;
2748
+ let base = [];
2749
+ if (u && typeof u.getSkillsRead === "function") {
2750
+ try {
2751
+ base = await Promise.resolve(u.getSkillsRead());
2752
+ } catch {
2753
+ }
2754
+ }
2755
+ const merged = /* @__PURE__ */ new Set([...base, ...this.skillsReadLocal]);
2756
+ return Array.from(merged);
2757
+ }
2628
2758
  async getItems(limit) {
2629
2759
  return this.underlyingSession.getItems(limit);
2630
2760
  }
@@ -2729,14 +2859,14 @@ async function main() {
2729
2859
  parseArgs();
2730
2860
  await ensureApiKey();
2731
2861
  sayHi();
2732
- const agent = trackedState.agent = new Agent3({
2862
+ const agent = trackedState.agent = new Agent2({
2733
2863
  name: "Harper App Development Assistant",
2734
2864
  model: isOpenAIModel(trackedState.model) ? trackedState.model : getModel(trackedState.model),
2735
2865
  modelSettings,
2736
2866
  instructions: readAgentsMD() || defaultInstructions(),
2737
2867
  tools: createTools()
2738
2868
  });
2739
- const session = createSession(trackedState.sessionPath);
2869
+ const session = trackedState.session = createSession(trackedState.sessionPath);
2740
2870
  while (true) {
2741
2871
  let task = "";
2742
2872
  let lastToolCallInfo = null;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@harperfast/agent",
3
3
  "description": "AI to help you with Harper app management",
4
- "version": "0.11.5",
4
+ "version": "0.12.1",
5
5
  "main": "dist/agent.js",
6
6
  "repository": "github:HarperFast/harper-agent",
7
7
  "bugs": {
@@ -10,11 +10,12 @@
10
10
  "homepage": "https://github.com/harperfast",
11
11
  "scripts": {
12
12
  "dev": "tsup agent.ts --format esm --clean --dts --watch",
13
+ "link": "npm run build && npm link",
13
14
  "build": "tsup agent.ts --format esm --clean --dts",
14
15
  "commitlint": "commitlint --edit",
15
16
  "start": "node ./dist/agent.js",
16
- "lint": "oxlint .",
17
- "lint:fix": "oxlint . --fix",
17
+ "lint": "oxlint --format stylish .",
18
+ "lint:fix": "oxlint --format stylish . --fix",
18
19
  "prepare": "husky",
19
20
  "format": "dprint check",
20
21
  "format:fix": "dprint fmt",