@ikarmic/forge-eval 0.0.0-stage → 4.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.
Files changed (103) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +95 -2
  3. package/dist/bin/forge-eval.d.ts +3 -0
  4. package/dist/bin/forge-eval.d.ts.map +1 -0
  5. package/dist/bin/forge-eval.js +138 -0
  6. package/dist/bin/forge-eval.js.map +1 -0
  7. package/dist/graders/behavior.d.ts +57 -0
  8. package/dist/graders/behavior.d.ts.map +1 -0
  9. package/dist/graders/behavior.js +154 -0
  10. package/dist/graders/behavior.js.map +1 -0
  11. package/dist/graders/content.d.ts +59 -0
  12. package/dist/graders/content.d.ts.map +1 -0
  13. package/dist/graders/content.js +135 -0
  14. package/dist/graders/content.js.map +1 -0
  15. package/dist/graders/fs.d.ts +24 -0
  16. package/dist/graders/fs.d.ts.map +1 -0
  17. package/dist/graders/fs.js +134 -0
  18. package/dist/graders/fs.js.map +1 -0
  19. package/dist/graders/index.d.ts +15 -0
  20. package/dist/graders/index.d.ts.map +1 -0
  21. package/dist/graders/index.js +19 -0
  22. package/dist/graders/index.js.map +1 -0
  23. package/dist/graders/metrics.d.ts +59 -0
  24. package/dist/graders/metrics.d.ts.map +1 -0
  25. package/dist/graders/metrics.js +70 -0
  26. package/dist/graders/metrics.js.map +1 -0
  27. package/dist/graders/schema.d.ts +87 -0
  28. package/dist/graders/schema.d.ts.map +1 -0
  29. package/dist/graders/schema.js +157 -0
  30. package/dist/graders/schema.js.map +1 -0
  31. package/dist/graders/shell.d.ts +36 -0
  32. package/dist/graders/shell.d.ts.map +1 -0
  33. package/dist/graders/shell.js +72 -0
  34. package/dist/graders/shell.js.map +1 -0
  35. package/dist/graders/text.d.ts +28 -0
  36. package/dist/graders/text.d.ts.map +1 -0
  37. package/dist/graders/text.js +70 -0
  38. package/dist/graders/text.js.map +1 -0
  39. package/dist/graders/types.d.ts +81 -0
  40. package/dist/graders/types.d.ts.map +1 -0
  41. package/dist/graders/types.js +2 -0
  42. package/dist/graders/types.js.map +1 -0
  43. package/dist/index.d.ts +8 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +9 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/report.d.ts +39 -0
  48. package/dist/report.d.ts.map +1 -0
  49. package/dist/report.js +120 -0
  50. package/dist/report.js.map +1 -0
  51. package/dist/runner.d.ts +59 -0
  52. package/dist/runner.d.ts.map +1 -0
  53. package/dist/runner.js +217 -0
  54. package/dist/runner.js.map +1 -0
  55. package/dist/tasks/edit.remove-section.d.ts +15 -0
  56. package/dist/tasks/edit.remove-section.d.ts.map +1 -0
  57. package/dist/tasks/edit.remove-section.js +31 -0
  58. package/dist/tasks/edit.remove-section.js.map +1 -0
  59. package/dist/tasks/edit.rename-symbol.d.ts +16 -0
  60. package/dist/tasks/edit.rename-symbol.d.ts.map +1 -0
  61. package/dist/tasks/edit.rename-symbol.js +34 -0
  62. package/dist/tasks/edit.rename-symbol.js.map +1 -0
  63. package/dist/tasks/edit.replace-greeting.d.ts +16 -0
  64. package/dist/tasks/edit.replace-greeting.d.ts.map +1 -0
  65. package/dist/tasks/edit.replace-greeting.js +29 -0
  66. package/dist/tasks/edit.replace-greeting.js.map +1 -0
  67. package/dist/tasks/edit.write-hello.d.ts +15 -0
  68. package/dist/tasks/edit.write-hello.d.ts.map +1 -0
  69. package/dist/tasks/edit.write-hello.js +22 -0
  70. package/dist/tasks/edit.write-hello.js.map +1 -0
  71. package/dist/tasks/index.d.ts +9 -0
  72. package/dist/tasks/index.d.ts.map +1 -0
  73. package/dist/tasks/index.js +37 -0
  74. package/dist/tasks/index.js.map +1 -0
  75. package/dist/tasks/memory.recall-relevant.d.ts +19 -0
  76. package/dist/tasks/memory.recall-relevant.d.ts.map +1 -0
  77. package/dist/tasks/memory.recall-relevant.js +36 -0
  78. package/dist/tasks/memory.recall-relevant.js.map +1 -0
  79. package/dist/tasks/multi.refactor-move.d.ts +16 -0
  80. package/dist/tasks/multi.refactor-move.d.ts.map +1 -0
  81. package/dist/tasks/multi.refactor-move.js +37 -0
  82. package/dist/tasks/multi.refactor-move.js.map +1 -0
  83. package/dist/tasks/multi.run-and-report.d.ts +16 -0
  84. package/dist/tasks/multi.run-and-report.d.ts.map +1 -0
  85. package/dist/tasks/multi.run-and-report.js +26 -0
  86. package/dist/tasks/multi.run-and-report.js.map +1 -0
  87. package/dist/tasks/multi.summarize.d.ts +16 -0
  88. package/dist/tasks/multi.summarize.d.ts.map +1 -0
  89. package/dist/tasks/multi.summarize.js +35 -0
  90. package/dist/tasks/multi.summarize.js.map +1 -0
  91. package/dist/tasks/read.list-files.d.ts +15 -0
  92. package/dist/tasks/read.list-files.d.ts.map +1 -0
  93. package/dist/tasks/read.list-files.js +28 -0
  94. package/dist/tasks/read.list-files.js.map +1 -0
  95. package/dist/tasks/read.package-version.d.ts +15 -0
  96. package/dist/tasks/read.package-version.d.ts.map +1 -0
  97. package/dist/tasks/read.package-version.js +25 -0
  98. package/dist/tasks/read.package-version.js.map +1 -0
  99. package/dist/tasks/read.search-todos.d.ts +16 -0
  100. package/dist/tasks/read.search-todos.d.ts.map +1 -0
  101. package/dist/tasks/read.search-todos.js +30 -0
  102. package/dist/tasks/read.search-todos.js.map +1 -0
  103. package/package.json +57 -3
@@ -0,0 +1,9 @@
1
+ import type { TaskDefinition } from '../graders/index.js';
2
+ /**
3
+ * The full task catalog. Order is the default run order. Keep grouped by
4
+ * category so the report reads naturally.
5
+ */
6
+ export declare const TASKS: readonly TaskDefinition[];
7
+ /** Lookup by id; returns undefined if missing. */
8
+ export declare function findTask(id: string): TaskDefinition | undefined;
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tasks/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAc1D;;;GAGG;AACH,eAAO,MAAM,KAAK,EAAE,SAAS,cAAc,EAgB1C,CAAC;AAEF,kDAAkD;AAClD,wBAAgB,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAE/D"}
@@ -0,0 +1,37 @@
1
+ import readListFiles from './read.list-files.js';
2
+ import readPackageVersion from './read.package-version.js';
3
+ import readSearchTodos from './read.search-todos.js';
4
+ import editWriteHello from './edit.write-hello.js';
5
+ import editReplaceGreeting from './edit.replace-greeting.js';
6
+ import editRemoveSection from './edit.remove-section.js';
7
+ import editRenameSymbol from './edit.rename-symbol.js';
8
+ import multiRunAndReport from './multi.run-and-report.js';
9
+ import multiRefactorMove from './multi.refactor-move.js';
10
+ import multiSummarize from './multi.summarize.js';
11
+ import memoryRecallRelevant from './memory.recall-relevant.js';
12
+ /**
13
+ * The full task catalog. Order is the default run order. Keep grouped by
14
+ * category so the report reads naturally.
15
+ */
16
+ export const TASKS = [
17
+ // read
18
+ readListFiles,
19
+ readPackageVersion,
20
+ readSearchTodos,
21
+ // edit
22
+ editWriteHello,
23
+ editReplaceGreeting,
24
+ editRemoveSection,
25
+ editRenameSymbol,
26
+ // multi-step
27
+ multiRunAndReport,
28
+ multiRefactorMove,
29
+ multiSummarize,
30
+ // memory (appended so the indexes of the tasks above stay stable)
31
+ memoryRecallRelevant,
32
+ ];
33
+ /** Lookup by id; returns undefined if missing. */
34
+ export function findTask(id) {
35
+ return TASKS.find((t) => t.id === id);
36
+ }
37
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tasks/index.ts"],"names":[],"mappings":"AAEA,OAAO,aAAa,MAAM,sBAAsB,CAAC;AACjD,OAAO,kBAAkB,MAAM,2BAA2B,CAAC;AAC3D,OAAO,eAAe,MAAM,wBAAwB,CAAC;AACrD,OAAO,cAAc,MAAM,uBAAuB,CAAC;AACnD,OAAO,mBAAmB,MAAM,4BAA4B,CAAC;AAC7D,OAAO,iBAAiB,MAAM,0BAA0B,CAAC;AACzD,OAAO,gBAAgB,MAAM,yBAAyB,CAAC;AACvD,OAAO,iBAAiB,MAAM,2BAA2B,CAAC;AAC1D,OAAO,iBAAiB,MAAM,0BAA0B,CAAC;AACzD,OAAO,cAAc,MAAM,sBAAsB,CAAC;AAClD,OAAO,oBAAoB,MAAM,6BAA6B,CAAC;AAE/D;;;GAGG;AACH,MAAM,CAAC,MAAM,KAAK,GAA8B;IAC9C,OAAO;IACP,aAAa;IACb,kBAAkB;IAClB,eAAe;IACf,OAAO;IACP,cAAc;IACd,mBAAmB;IACnB,iBAAiB;IACjB,gBAAgB;IAChB,aAAa;IACb,iBAAiB;IACjB,iBAAiB;IACjB,cAAc;IACd,kEAAkE;IAClE,oBAAoB;CACrB,CAAC;AAEF,kDAAkD;AAClD,MAAM,UAAU,QAAQ,CAAC,EAAU;IACjC,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;AACxC,CAAC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * memory / recall-relevant: the project's memory is larger than the prompt
3
+ * budget, so only an index plus the entries relevant to the question reach the
4
+ * model. The answer lives in one `correction` section ("use pnpm, never npm").
5
+ * Another section names a file that no longer exists, so it is injected with
6
+ * the "possibly stale" marker. Opts into memory (the only task that does).
7
+ */
8
+ declare const _default: {
9
+ id: string;
10
+ name: string;
11
+ category: "read";
12
+ description: string;
13
+ task: string;
14
+ memory: true;
15
+ setup: (cwd: string) => Promise<void>;
16
+ graders: import("../index.js").Grader[];
17
+ };
18
+ export default _default;
19
+ //# sourceMappingURL=memory.recall-relevant.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory.recall-relevant.d.ts","sourceRoot":"","sources":["../../src/tasks/memory.recall-relevant.ts"],"names":[],"mappings":"AAOA;;;;;;GAMG;;;;;;;;;;;AACH,wBAuBG"}
@@ -0,0 +1,36 @@
1
+ import { mkdir, writeFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { defineTask, regex, toolNotCalled, completed } from '../graders/index.js';
4
+ const FILLER_SENTENCE = 'Notes about this subsystem that have nothing to do with how anything is built or run. ';
5
+ /**
6
+ * memory / recall-relevant: the project's memory is larger than the prompt
7
+ * budget, so only an index plus the entries relevant to the question reach the
8
+ * model. The answer lives in one `correction` section ("use pnpm, never npm").
9
+ * Another section names a file that no longer exists, so it is injected with
10
+ * the "possibly stale" marker. Opts into memory (the only task that does).
11
+ */
12
+ export default defineTask({
13
+ id: 'memory-recall-relevant',
14
+ name: 'Recall the right memory entry',
15
+ category: 'read',
16
+ description: 'Agent answers from project memory: pnpm correction wins; the stale entry is flagged, not trusted.',
17
+ task: 'Which command runs the tests in this project? Reply with just the command, nothing else.',
18
+ memory: true,
19
+ setup: async (cwd) => {
20
+ await mkdir(join(cwd, '.forge'), { recursive: true });
21
+ await writeFile(join(cwd, 'package.json'), JSON.stringify({ name: 'sample', version: '1.0.0' }, null, 2));
22
+ const filler = Array.from({ length: 10 }, (_, i) => {
23
+ const n = String(i + 1).padStart(2, '0');
24
+ return `## Subsystem ${n}\n\n<!-- forge: type=convention, updated=2026-09-01 -->\n${FILLER_SENTENCE.repeat(10)}`;
25
+ });
26
+ const memory = [
27
+ '# Project Memory',
28
+ ...filler,
29
+ '## Package manager\n\n<!-- forge: type=correction, updated=2026-10-01, refs=package.json -->\nUse pnpm, never npm. Run `pnpm test` to run the tests in this workspace.',
30
+ '## Legacy API\n\n<!-- forge: type=decision, updated=2026-08-01, refs=src/legacy/api.ts, pin=true -->\nNever call the legacy API directly; go through the gateway.',
31
+ ].join('\n\n');
32
+ await writeFile(join(cwd, '.forge', 'MEMORY.md'), `${memory}\n`);
33
+ },
34
+ graders: [completed(), regex(/pnpm test/), toolNotCalled('run_command')],
35
+ });
36
+ //# sourceMappingURL=memory.recall-relevant.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory.recall-relevant.js","sourceRoot":"","sources":["../../src/tasks/memory.recall-relevant.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAElF,MAAM,eAAe,GAAG,wFAAwF,CAAC;AAEjH;;;;;;GAMG;AACH,eAAe,UAAU,CAAC;IACxB,EAAE,EAAE,wBAAwB;IAC5B,IAAI,EAAE,+BAA+B;IACrC,QAAQ,EAAE,MAAM;IAChB,WAAW,EAAE,mGAAmG;IAChH,IAAI,EAAE,0FAA0F;IAChG,MAAM,EAAE,IAAI;IACZ,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QACnB,MAAM,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACtD,MAAM,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC1G,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;YACjD,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;YACzC,OAAO,gBAAgB,CAAC,4DAA4D,eAAe,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;QACnH,CAAC,CAAC,CAAC;QACH,MAAM,MAAM,GAAG;YACb,kBAAkB;YAClB,GAAG,MAAM;YACT,wKAAwK;YACxK,mKAAmK;SACpK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACf,MAAM,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,EAAE,WAAW,CAAC,EAAE,GAAG,MAAM,IAAI,CAAC,CAAC;IACnE,CAAC;IACD,OAAO,EAAE,CAAC,SAAS,EAAE,EAAE,KAAK,CAAC,WAAW,CAAC,EAAE,aAAa,CAAC,aAAa,CAAC,CAAC;CACzE,CAAC,CAAC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * multi-step / refactor-move: agent must move a function from one file to
3
+ * another and remove it from the source. Tests reading + writing + editing
4
+ * in a coordinated way.
5
+ */
6
+ declare const _default: {
7
+ id: string;
8
+ name: string;
9
+ category: "multi-step";
10
+ description: string;
11
+ task: string;
12
+ setup: (cwd: string) => Promise<void>;
13
+ graders: import("../index.js").Grader[];
14
+ };
15
+ export default _default;
16
+ //# sourceMappingURL=multi.refactor-move.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multi.refactor-move.d.ts","sourceRoot":"","sources":["../../src/tasks/multi.refactor-move.ts"],"names":[],"mappings":"AAKA;;;;GAIG;;;;;;;;;;AACH,wBAkCG"}
@@ -0,0 +1,37 @@
1
+ import { mkdir, writeFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { defineTask, fileMatches, fileMissing, toolWasCalled, completed, commandRuns } from '../graders/index.js';
4
+ /**
5
+ * multi-step / refactor-move: agent must move a function from one file to
6
+ * another and remove it from the source. Tests reading + writing + editing
7
+ * in a coordinated way.
8
+ */
9
+ export default defineTask({
10
+ id: 'multi-refactor-move',
11
+ name: 'Move function between files',
12
+ category: 'multi-step',
13
+ description: 'Agent moves a function from a.ts to b.ts and removes it from a.ts.',
14
+ task: 'The current directory has two files: `a.ts` and `b.ts`. `a.ts` exports a ' +
15
+ 'function called `greet`. Move the `greet` function (and only that function) ' +
16
+ 'into `b.ts`. Update `a.ts` so the function is no longer defined there, but ' +
17
+ 'leave any other exports in `a.ts` alone. `b.ts` should end up declaring and ' +
18
+ 'exporting `greet`.',
19
+ setup: async (cwd) => {
20
+ await mkdir(cwd, { recursive: true });
21
+ await writeFile(join(cwd, 'a.ts'), "export function greet(name: string): string { return `Hello, ${name}!`; }\n" +
22
+ "export const VERSION = '1.0';\n");
23
+ await writeFile(join(cwd, 'b.ts'), "// b.ts placeholder\nexport const PLACEHOLDER = true;\n");
24
+ },
25
+ graders: [
26
+ completed(),
27
+ toolWasCalled('read_file'),
28
+ fileMatches('b.ts', 'function greet'),
29
+ fileMatches('a.ts', 'VERSION'),
30
+ // `a.ts` must no longer define the function. Graded on the outcome, so write_file and edit_file both count.
31
+ commandRuns({
32
+ command: ['node', '-e', "process.exit(require('fs').readFileSync('a.ts', 'utf8').includes('function greet') ? 1 : 0)"],
33
+ }),
34
+ fileMissing('a-greet-backup.ts'),
35
+ ],
36
+ });
37
+ //# sourceMappingURL=multi.refactor-move.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multi.refactor-move.js","sourceRoot":"","sources":["../../src/tasks/multi.refactor-move.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAElH;;;;GAIG;AACH,eAAe,UAAU,CAAC;IACxB,EAAE,EAAE,qBAAqB;IACzB,IAAI,EAAE,6BAA6B;IACnC,QAAQ,EAAE,YAAY;IACtB,WAAW,EAAE,oEAAoE;IACjF,IAAI,EACF,2EAA2E;QAC3E,8EAA8E;QAC9E,6EAA6E;QAC7E,8EAA8E;QAC9E,oBAAoB;IACtB,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QACnB,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACtC,MAAM,SAAS,CACb,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,EACjB,6EAA6E;YAC3E,iCAAiC,CACpC,CAAC;QACF,MAAM,SAAS,CACb,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,EACjB,yDAAyD,CAC1D,CAAC;IACJ,CAAC;IACD,OAAO,EAAE;QACP,SAAS,EAAE;QACX,aAAa,CAAC,WAAW,CAAC;QAC1B,WAAW,CAAC,MAAM,EAAE,gBAAgB,CAAC;QACrC,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC;QAC9B,4GAA4G;QAC5G,WAAW,CAAC;YACV,OAAO,EAAE,CAAC,MAAM,EAAE,IAAI,EAAE,6FAA6F,CAAC;SACvH,CAAC;QACF,WAAW,CAAC,mBAAmB,CAAC;KACjC;CACF,CAAC,CAAC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * multi-step / run-and-report: agent must run a command and report its
3
+ * output. Verifies the model uses run_command (not just hallucinating the
4
+ * answer) and that it surfaces the result correctly.
5
+ */
6
+ declare const _default: {
7
+ id: string;
8
+ name: string;
9
+ category: "multi-step";
10
+ description: string;
11
+ task: string;
12
+ setup: () => Promise<void>;
13
+ graders: import("../index.js").Grader[];
14
+ };
15
+ export default _default;
16
+ //# sourceMappingURL=multi.run-and-report.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multi.run-and-report.d.ts","sourceRoot":"","sources":["../../src/tasks/multi.run-and-report.ts"],"names":[],"mappings":"AAEA;;;;GAIG;;;;;;;;;;AACH,wBAmBG"}
@@ -0,0 +1,26 @@
1
+ import { defineTask, commandRuns, contains, toolWasCalled, completed } from '../graders/index.js';
2
+ /**
3
+ * multi-step / run-and-report: agent must run a command and report its
4
+ * output. Verifies the model uses run_command (not just hallucinating the
5
+ * answer) and that it surfaces the result correctly.
6
+ */
7
+ export default defineTask({
8
+ id: 'multi-run-and-report',
9
+ name: 'Run a command and report output',
10
+ category: 'multi-step',
11
+ description: 'Agent runs a shell command and reports its stdout.',
12
+ task: 'Run the command `node -e "console.log(2 + 2)"` in the current directory using ' +
13
+ 'the run_command tool and tell me what it printed.',
14
+ setup: async () => {
15
+ // No fixtures needed.
16
+ },
17
+ graders: [
18
+ completed(),
19
+ toolWasCalled('run_command'),
20
+ // Sanity: the command grader proves the command actually works in cwd.
21
+ commandRuns({ command: ['node', '-e', 'console.log(2 + 2)'], contains: '4' }),
22
+ // The model's reply should mention 4 too.
23
+ contains('4'),
24
+ ],
25
+ });
26
+ //# sourceMappingURL=multi.run-and-report.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multi.run-and-report.js","sourceRoot":"","sources":["../../src/tasks/multi.run-and-report.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,QAAQ,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAElG;;;;GAIG;AACH,eAAe,UAAU,CAAC;IACxB,EAAE,EAAE,sBAAsB;IAC1B,IAAI,EAAE,iCAAiC;IACvC,QAAQ,EAAE,YAAY;IACtB,WAAW,EAAE,oDAAoD;IACjE,IAAI,EACF,gFAAgF;QAChF,mDAAmD;IACrD,KAAK,EAAE,KAAK,IAAI,EAAE;QAChB,sBAAsB;IACxB,CAAC;IACD,OAAO,EAAE;QACP,SAAS,EAAE;QACX,aAAa,CAAC,aAAa,CAAC;QAC5B,uEAAuE;QACvE,WAAW,CAAC,EAAE,OAAO,EAAE,CAAC,MAAM,EAAE,IAAI,EAAE,oBAAoB,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC;QAC7E,0CAA0C;QAC1C,QAAQ,CAAC,GAAG,CAAC;KACd;CACF,CAAC,CAAC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * multi-step / summarize: agent must read two files and produce a one-line
3
+ * summary that includes specific facts from both. Verifies it actually
4
+ * read each file (not just guessed) by checking tool calls + output tokens.
5
+ */
6
+ declare const _default: {
7
+ id: string;
8
+ name: string;
9
+ category: "multi-step";
10
+ description: string;
11
+ task: string;
12
+ setup: (cwd: string) => Promise<void>;
13
+ graders: import("../index.js").Grader[];
14
+ };
15
+ export default _default;
16
+ //# sourceMappingURL=multi.summarize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multi.summarize.d.ts","sourceRoot":"","sources":["../../src/tasks/multi.summarize.ts"],"names":[],"mappings":"AAKA;;;;GAIG;;;;;;;;;;AACH,wBAgCG"}
@@ -0,0 +1,35 @@
1
+ import { mkdir, writeFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { defineTask, regex, contains, toolWasCalled, completed } from '../graders/index.js';
4
+ /**
5
+ * multi-step / summarize: agent must read two files and produce a one-line
6
+ * summary that includes specific facts from both. Verifies it actually
7
+ * read each file (not just guessed) by checking tool calls + output tokens.
8
+ */
9
+ export default defineTask({
10
+ id: 'multi-summarize-two-files',
11
+ name: 'Summarize two files',
12
+ category: 'multi-step',
13
+ description: 'Agent reads two files and summarizes them in one sentence.',
14
+ task: 'Read the files `alpha.txt` and `beta.txt` in the current directory. In a ' +
15
+ 'single short sentence (no more than 30 words), summarize what each file ' +
16
+ 'is about. Mention the project name "Astra" and the year "2030" in your reply.',
17
+ setup: async (cwd) => {
18
+ await mkdir(cwd, { recursive: true });
19
+ await writeFile(join(cwd, 'alpha.txt'), 'Project Astra is the company-wide initiative launched in 2030 to unify our ' +
20
+ 'disconnected tools into one platform.\n');
21
+ await writeFile(join(cwd, 'beta.txt'), 'The 2030 roadmap for Astra focuses on three pillars: developer experience, ' +
22
+ 'security, and cost.\n');
23
+ },
24
+ graders: [
25
+ completed(),
26
+ toolWasCalled('read_file', 2),
27
+ // Both files were read: check the model called read_file at least twice
28
+ // (the tool was called) AND that the result mentions both key terms.
29
+ contains('Astra'),
30
+ contains('2030'),
31
+ // The reply should be reasonably short: not a multi-page essay.
32
+ regex(/\b\w+\b.*\b\w+\b/s),
33
+ ],
34
+ });
35
+ //# sourceMappingURL=multi.summarize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multi.summarize.js","sourceRoot":"","sources":["../../src/tasks/multi.summarize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAE5F;;;;GAIG;AACH,eAAe,UAAU,CAAC;IACxB,EAAE,EAAE,2BAA2B;IAC/B,IAAI,EAAE,qBAAqB;IAC3B,QAAQ,EAAE,YAAY;IACtB,WAAW,EAAE,4DAA4D;IACzE,IAAI,EACF,2EAA2E;QAC3E,0EAA0E;QAC1E,+EAA+E;IACjF,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QACnB,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACtC,MAAM,SAAS,CACb,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,EACtB,6EAA6E;YAC3E,yCAAyC,CAC5C,CAAC;QACF,MAAM,SAAS,CACb,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,EACrB,6EAA6E;YAC3E,uBAAuB,CAC1B,CAAC;IACJ,CAAC;IACD,OAAO,EAAE;QACP,SAAS,EAAE;QACX,aAAa,CAAC,WAAW,EAAE,CAAC,CAAC;QAC7B,wEAAwE;QACxE,qEAAqE;QACrE,QAAQ,CAAC,OAAO,CAAC;QACjB,QAAQ,CAAC,MAAM,CAAC;QAChB,gEAAgE;QAChE,KAAK,CAAC,mBAAmB,CAAC;KAC3B;CACF,CAAC,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * read / list-files: ask the agent to enumerate TypeScript files in the
3
+ * project root and report what it found.
4
+ */
5
+ declare const _default: {
6
+ id: string;
7
+ name: string;
8
+ category: "read";
9
+ description: string;
10
+ task: string;
11
+ setup: (cwd: string) => Promise<void>;
12
+ graders: import("../index.js").Grader[];
13
+ };
14
+ export default _default;
15
+ //# sourceMappingURL=read.list-files.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read.list-files.d.ts","sourceRoot":"","sources":["../../src/tasks/read.list-files.ts"],"names":[],"mappings":"AAKA;;;GAGG;;;;;;;;;;AACH,wBAoBG"}
@@ -0,0 +1,28 @@
1
+ import { mkdir, writeFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { defineTask, regex, toolWasCalled, completed } from '../graders/index.js';
4
+ /**
5
+ * read / list-files: ask the agent to enumerate TypeScript files in the
6
+ * project root and report what it found.
7
+ */
8
+ export default defineTask({
9
+ id: 'read-list-typescript-files',
10
+ name: 'List TypeScript files',
11
+ category: 'read',
12
+ description: 'Agent enumerates .ts files under the project root.',
13
+ task: 'Use the list_files tool to find all TypeScript files (extension `.ts`) in the ' +
14
+ 'current directory and report their names. Do not recurse into node_modules.',
15
+ setup: async (cwd) => {
16
+ await mkdir(cwd, { recursive: true });
17
+ await writeFile(join(cwd, 'a.ts'), "export const a = 1;\n");
18
+ await writeFile(join(cwd, 'b.ts'), "export const b = 2;\n");
19
+ await writeFile(join(cwd, 'README.md'), '# not ts\n');
20
+ await writeFile(join(cwd, 'package.json'), '{}\n');
21
+ },
22
+ graders: [
23
+ completed(),
24
+ toolWasCalled('list_files'),
25
+ regex(/\.ts\b/),
26
+ ],
27
+ });
28
+ //# sourceMappingURL=read.list-files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read.list-files.js","sourceRoot":"","sources":["../../src/tasks/read.list-files.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAElF;;;GAGG;AACH,eAAe,UAAU,CAAC;IACxB,EAAE,EAAE,4BAA4B;IAChC,IAAI,EAAE,uBAAuB;IAC7B,QAAQ,EAAE,MAAM;IAChB,WAAW,EAAE,oDAAoD;IACjE,IAAI,EACF,gFAAgF;QAChF,6EAA6E;IAC/E,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QACnB,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACtC,MAAM,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,EAAE,uBAAuB,CAAC,CAAC;QAC5D,MAAM,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,EAAE,uBAAuB,CAAC,CAAC;QAC5D,MAAM,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,EAAE,YAAY,CAAC,CAAC;QACtD,MAAM,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAAC;IACrD,CAAC;IACD,OAAO,EAAE;QACP,SAAS,EAAE;QACX,aAAa,CAAC,YAAY,CAAC;QAC3B,KAAK,CAAC,QAAQ,CAAC;KAChB;CACF,CAAC,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * read / package-version: the agent must read package.json and surface the
3
+ * version field. Verifies the model actually reads the file (not just guesses).
4
+ */
5
+ declare const _default: {
6
+ id: string;
7
+ name: string;
8
+ category: "read";
9
+ description: string;
10
+ task: string;
11
+ setup: (cwd: string) => Promise<void>;
12
+ graders: import("../index.js").Grader[];
13
+ };
14
+ export default _default;
15
+ //# sourceMappingURL=read.package-version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read.package-version.d.ts","sourceRoot":"","sources":["../../src/tasks/read.package-version.ts"],"names":[],"mappings":"AAKA;;;GAGG;;;;;;;;;;AACH,wBAoBG"}
@@ -0,0 +1,25 @@
1
+ import { mkdir, writeFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { defineTask, regex, toolWasCalled, completed } from '../graders/index.js';
4
+ /**
5
+ * read / package-version: the agent must read package.json and surface the
6
+ * version field. Verifies the model actually reads the file (not just guesses).
7
+ */
8
+ export default defineTask({
9
+ id: 'read-package-version',
10
+ name: 'Read package.json version',
11
+ category: 'read',
12
+ description: 'Agent reads package.json and reports the version string.',
13
+ task: 'Read the file `package.json` in the current directory and tell me the value of ' +
14
+ 'the "version" field. Reply with just the version string, nothing else.',
15
+ setup: async (cwd) => {
16
+ await mkdir(cwd, { recursive: true });
17
+ await writeFile(join(cwd, 'package.json'), JSON.stringify({ name: 'sample', version: '4.2.1', description: 'x' }, null, 2));
18
+ },
19
+ graders: [
20
+ completed(),
21
+ toolWasCalled('read_file'),
22
+ regex(/4\.2\.1/),
23
+ ],
24
+ });
25
+ //# sourceMappingURL=read.package-version.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read.package-version.js","sourceRoot":"","sources":["../../src/tasks/read.package-version.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAElF;;;GAGG;AACH,eAAe,UAAU,CAAC;IACxB,EAAE,EAAE,sBAAsB;IAC1B,IAAI,EAAE,2BAA2B;IACjC,QAAQ,EAAE,MAAM;IAChB,WAAW,EAAE,0DAA0D;IACvE,IAAI,EACF,iFAAiF;QACjF,wEAAwE;IAC1E,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QACnB,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACtC,MAAM,SAAS,CACb,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,EACzB,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAChF,CAAC;IACJ,CAAC;IACD,OAAO,EAAE;QACP,SAAS,EAAE;QACX,aAAa,CAAC,WAAW,CAAC;QAC1B,KAAK,CAAC,SAAS,CAAC;KACjB;CACF,CAAC,CAAC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * read / search-todos: agent searches the workspace for TODO comments.
3
+ * We seed three TODO markers in three files (plus a decoy without one)
4
+ * so a model that only checks one file fails.
5
+ */
6
+ declare const _default: {
7
+ id: string;
8
+ name: string;
9
+ category: "read";
10
+ description: string;
11
+ task: string;
12
+ setup: (cwd: string) => Promise<void>;
13
+ graders: import("../index.js").Grader[];
14
+ };
15
+ export default _default;
16
+ //# sourceMappingURL=read.search-todos.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read.search-todos.d.ts","sourceRoot":"","sources":["../../src/tasks/read.search-todos.ts"],"names":[],"mappings":"AAKA;;;;GAIG;;;;;;;;;;AACH,wBAqBG"}
@@ -0,0 +1,30 @@
1
+ import { mkdir, writeFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { defineTask, regex, toolWasCalled, completed } from '../graders/index.js';
4
+ /**
5
+ * read / search-todos: agent searches the workspace for TODO comments.
6
+ * We seed three TODO markers in three files (plus a decoy without one)
7
+ * so a model that only checks one file fails.
8
+ */
9
+ export default defineTask({
10
+ id: 'read-search-todos',
11
+ name: 'Search for TODO comments',
12
+ category: 'read',
13
+ description: 'Agent searches for TODO comments across files.',
14
+ task: 'Search the current directory for files containing the text "TODO" and list ' +
15
+ 'the file paths you find. Use the search_files tool: do not read files manually.',
16
+ setup: async (cwd) => {
17
+ await mkdir(cwd, { recursive: true });
18
+ await writeFile(join(cwd, 'alpha.ts'), '// TODO: implement alpha\nconst a = 1;\n');
19
+ await writeFile(join(cwd, 'beta.ts'), 'export function beta() { /* TODO later */ }\n');
20
+ await writeFile(join(cwd, 'gamma.ts'), '// no todos here\nconst g = 3;\n');
21
+ await writeFile(join(cwd, 'README.md'), '# Project\n');
22
+ },
23
+ graders: [
24
+ completed(),
25
+ toolWasCalled('search_files'),
26
+ regex(/alpha\.ts/),
27
+ regex(/beta\.ts/),
28
+ ],
29
+ });
30
+ //# sourceMappingURL=read.search-todos.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read.search-todos.js","sourceRoot":"","sources":["../../src/tasks/read.search-todos.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAElF;;;;GAIG;AACH,eAAe,UAAU,CAAC;IACxB,EAAE,EAAE,mBAAmB;IACvB,IAAI,EAAE,0BAA0B;IAChC,QAAQ,EAAE,MAAM;IAChB,WAAW,EAAE,gDAAgD;IAC7D,IAAI,EACF,6EAA6E;QAC7E,iFAAiF;IACnF,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QACnB,MAAM,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACtC,MAAM,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,EAAE,0CAA0C,CAAC,CAAC;QACnF,MAAM,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,EAAE,+CAA+C,CAAC,CAAC;QACvF,MAAM,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,EAAE,kCAAkC,CAAC,CAAC;QAC3E,MAAM,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,EAAE,aAAa,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,EAAE;QACP,SAAS,EAAE;QACX,aAAa,CAAC,cAAc,CAAC;QAC7B,KAAK,CAAC,WAAW,CAAC;QAClB,KAAK,CAAC,UAAU,CAAC;KAClB;CACF,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,60 @@
1
1
  {
2
2
  "name": "@ikarmic/forge-eval",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "4.5.0",
4
+ "description": "Eval harness for IKarmic AI Forge. Runs agent tasks against isolated workspaces and grades the results with deterministic checks.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "bin": {
15
+ "forge-eval": "./dist/bin/forge-eval.js"
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "README.md"
20
+ ],
21
+ "keywords": [
22
+ "ai",
23
+ "eval",
24
+ "benchmark",
25
+ "agent",
26
+ "llm",
27
+ "code-assistant",
28
+ "testing"
29
+ ],
30
+ "license": "MIT",
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "https://github.com/ikarmicai/ikarmic-forge"
34
+ },
35
+ "bugs": {
36
+ "url": "https://github.com/ikarmicai/ikarmic-forge/issues"
37
+ },
38
+ "homepage": "https://github.com/ikarmicai/ikarmic-forge/tree/main/packages/eval#readme",
39
+ "engines": {
40
+ "node": ">=22"
41
+ },
42
+ "dependencies": {
43
+ "ai": "^7.0.0",
44
+ "zod": "^3.23.8",
45
+ "@ikarmic/forge-core": "4.5.0"
46
+ },
47
+ "devDependencies": {
48
+ "@types/node": "^22.7.0",
49
+ "tsx": "^4.19.2",
50
+ "typescript": "^5.6.3",
51
+ "vitest": "^5.0.2"
52
+ },
53
+ "scripts": {
54
+ "build": "tsc -p tsconfig.json",
55
+ "dev": "tsx src/bin/forge-eval.ts",
56
+ "test": "vitest run",
57
+ "typecheck": "tsc -p tsconfig.json --noEmit",
58
+ "clean": "rm -rf dist *.tsbuildinfo"
59
+ }
6
60
  }