@agjs/tsforge 0.1.4 → 0.1.6

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/package.json +1 -1
  2. package/src/loop/run.ts +40 -4
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@agjs/tsforge",
3
3
  "type": "module",
4
- "version": "0.1.4",
4
+ "version": "0.1.6",
5
5
  "license": "MIT",
6
6
  "description": "TypeScript coding harness with a deterministic gate, stack-aware guardrails, and stream-level correction.",
7
7
  "repository": {
package/src/loop/run.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { join } from "node:path";
1
2
  import type { ITask } from "../spec";
2
3
  import type { IChatMessage, IModelResponse, IProvider } from "../inference";
3
4
  import { validate, type ErrorParser } from "../validate";
@@ -8,7 +9,7 @@ import type { IRunResult, IRunOptions, Reporter } from "./loop.types";
8
9
  import { flags } from "../config";
9
10
  import { SYSTEM, seedPrompt } from "./prompt";
10
11
  import { detectStack } from "../stack-detection";
11
- import { TtsrManager } from "./ttsr";
12
+ import { TtsrManager, parseProjectRules, type ITtsrRule } from "./ttsr";
12
13
  import { DEFAULT_TTSR_RULES } from "./ttsr-defaults";
13
14
  import {
14
15
  type ILoopCtx,
@@ -65,8 +66,27 @@ function handleDegeneration(
65
66
  };
66
67
  }
67
68
 
68
- /** Build and configure a TTSR manager if enabled. Returns null if disabled. */
69
- function initTtsrManager(): TtsrManager | null {
69
+ /** Read and parse `<cwd>/.tsforge/rules.json` if present. Missing or invalid
70
+ * files yield no rules (parseProjectRules tolerates malformed JSON). */
71
+ export async function loadProjectTtsrRules(cwd: string): Promise<ITtsrRule[]> {
72
+ const file = Bun.file(join(cwd, ".tsforge", "rules.json"));
73
+
74
+ if (!(await file.exists())) {
75
+ return [];
76
+ }
77
+
78
+ return parseProjectRules(await file.text());
79
+ }
80
+
81
+ /** Build and configure a TTSR manager if enabled. Returns null if disabled.
82
+ * Built-in defaults register first, then optional project rules from
83
+ * `<cwd>/.tsforge/rules.json`; `addRule` ignores duplicate names, so a
84
+ * built-in safety rule always wins over a same-named project rule. */
85
+ export async function initTtsrManager(
86
+ cwd: string,
87
+ report: Reporter,
88
+ taskId: string
89
+ ): Promise<TtsrManager | null> {
70
90
  if (!flags.ttsr()) {
71
91
  return null;
72
92
  }
@@ -77,6 +97,22 @@ function initTtsrManager(): TtsrManager | null {
77
97
  manager.addRule(rule);
78
98
  }
79
99
 
100
+ let added = 0;
101
+
102
+ for (const rule of await loadProjectTtsrRules(cwd)) {
103
+ if (manager.addRule(rule)) {
104
+ added += 1;
105
+ }
106
+ }
107
+
108
+ if (added > 0) {
109
+ report({
110
+ kind: "ttsr",
111
+ task: taskId,
112
+ message: `loaded ${added} custom TTSR rule(s) from .tsforge/rules.json`,
113
+ });
114
+ }
115
+
80
116
  return manager;
81
117
  }
82
118
 
@@ -262,7 +298,7 @@ export async function runTask(
262
298
  thinkingTokenBudget ??
263
299
  (hasExistingCode ? undefined : LOOP_LIMITS.scratchThinkingBudget);
264
300
 
265
- const ttsrManager = initTtsrManager();
301
+ const ttsrManager = await initTtsrManager(cwd, report, task.id);
266
302
 
267
303
  const ctx: ILoopCtx = {
268
304
  task,