@hasna/loops 0.3.7 → 0.3.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -2953,7 +2953,8 @@ function planPrompt(spec) {
2953
2953
  const budget = spec.tokenBudget ? `Token budget: ${spec.tokenBudget}.` : "No explicit token budget.";
2954
2954
  return [
2955
2955
  "Create a flat DAG goal plan for this objective.",
2956
- "Each node must have a stable short key, a concrete objective, optional dependsOn keys, and optional priority.",
2956
+ "Each node must have a stable short key, a concrete objective, dependsOn keys, priority, and tokenBudget.",
2957
+ "Use dependsOn: [] when there are no dependencies, priority: 0 when no priority is needed, and tokenBudget: null when no per-node budget is needed.",
2957
2958
  "Prefer the smallest plan that can prove the explicit requirements.",
2958
2959
  budget,
2959
2960
  `Objective: ${spec.objective}`
@@ -2965,6 +2966,9 @@ function achievementPrompt(goal, nodes, evidence) {
2965
2966
  "Run an adversarial achievement audit.",
2966
2967
  "Completion is unproven until every explicit requirement is verified against evidence.",
2967
2968
  "Return achieved=false if evidence is missing, ambiguous, or only asserts completion.",
2969
+ "Return achieved=true when the evidence proves every explicit requirement; do not return achieved=false with an empty unmetRequirements array.",
2970
+ "Return status as complete, active, blocked, budgetLimited, cancelled, or null when no status change is appropriate.",
2971
+ "Return evidence and unmetRequirements as arrays, using [] when empty.",
2968
2972
  "adversarialReview must be non-empty and must describe the attempted falsification.",
2969
2973
  `Goal: ${goal.objective}`,
2970
2974
  `Nodes: ${nodes.map((node) => `${node.key}=${node.status}`).join(", ")}`,
@@ -2981,18 +2985,18 @@ var DEFAULT_MAX_TURNS = 10;
2981
2985
  var PlanNodeSchema = z.object({
2982
2986
  key: z.string().min(1).max(64).regex(/^[A-Za-z0-9_.-]+$/),
2983
2987
  objective: z.string().min(1),
2984
- dependsOn: z.array(z.string().min(1)).optional().default([]),
2985
- priority: z.number().int().optional().default(0),
2986
- tokenBudget: z.number().int().positive().optional()
2988
+ dependsOn: z.array(z.string().min(1)),
2989
+ priority: z.number().int(),
2990
+ tokenBudget: z.number().int().positive().nullable()
2987
2991
  });
2988
2992
  var PlanSchema = z.object({
2989
2993
  nodes: z.array(PlanNodeSchema).min(1)
2990
2994
  });
2991
2995
  var AchievementSchema = z.object({
2992
2996
  achieved: z.boolean(),
2993
- status: z.enum(["active", "blocked", "budgetLimited", "complete", "cancelled"]).optional(),
2994
- evidence: z.array(z.string()).optional().default([]),
2995
- unmetRequirements: z.array(z.string()).optional().default([]),
2997
+ status: z.enum(["active", "blocked", "budgetLimited", "complete", "cancelled"]).nullable(),
2998
+ evidence: z.array(z.string()),
2999
+ unmetRequirements: z.array(z.string()),
2996
3000
  adversarialReview: z.string().min(1)
2997
3001
  });
2998
3002
  function normalizeGoalSpec2(spec) {
@@ -3076,7 +3080,7 @@ async function planGoal(store, goal, spec, model, opts) {
3076
3080
  objective: node.objective,
3077
3081
  dependsOn: node.dependsOn ?? [],
3078
3082
  priority: node.priority ?? 0,
3079
- tokenBudget: node.tokenBudget,
3083
+ tokenBudget: node.tokenBudget ?? undefined,
3080
3084
  sequence: index
3081
3085
  }));
3082
3086
  assertAcyclicNodes(rawNodes.map((node) => ({ key: node.key, dependsOn: node.dependsOn })));
@@ -4213,10 +4217,99 @@ function runDoctor(store) {
4213
4217
  checks
4214
4218
  };
4215
4219
  }
4220
+ // package.json
4221
+ var package_default = {
4222
+ name: "@hasna/loops",
4223
+ version: "0.3.9",
4224
+ description: "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
4225
+ type: "module",
4226
+ main: "dist/index.js",
4227
+ types: "dist/index.d.ts",
4228
+ bin: {
4229
+ loops: "dist/cli/index.js",
4230
+ "loops-daemon": "dist/daemon/index.js"
4231
+ },
4232
+ exports: {
4233
+ ".": {
4234
+ types: "./dist/index.d.ts",
4235
+ import: "./dist/index.js"
4236
+ },
4237
+ "./sdk": {
4238
+ types: "./dist/sdk/index.d.ts",
4239
+ import: "./dist/sdk/index.js"
4240
+ },
4241
+ "./storage": {
4242
+ types: "./dist/lib/store.d.ts",
4243
+ import: "./dist/lib/store.js"
4244
+ }
4245
+ },
4246
+ files: [
4247
+ "dist",
4248
+ "README.md",
4249
+ "docs",
4250
+ "LICENSE"
4251
+ ],
4252
+ scripts: {
4253
+ build: "rm -rf dist && bun build src/cli/index.ts src/daemon/index.ts src/index.ts src/sdk/index.ts src/lib/store.ts --outdir dist --target bun --packages external && chmod +x dist/cli/index.js dist/daemon/index.js && tsc -p tsconfig.build.json --emitDeclarationOnly --outDir dist",
4254
+ "build:bin": "bun build src/cli/index.ts --compile --outfile dist/loops",
4255
+ typecheck: "tsc --noEmit",
4256
+ test: "bun test",
4257
+ prepare: "bun run build",
4258
+ "dev:cli": "bun run src/cli/index.ts",
4259
+ "dev:daemon": "bun run src/daemon/index.ts",
4260
+ prepublishOnly: "bun run build"
4261
+ },
4262
+ keywords: [
4263
+ "loops",
4264
+ "scheduler",
4265
+ "daemon",
4266
+ "agents",
4267
+ "claude",
4268
+ "cursor",
4269
+ "codewith",
4270
+ "opencode",
4271
+ "cli"
4272
+ ],
4273
+ repository: {
4274
+ type: "git",
4275
+ url: "git+https://github.com/hasna/loops.git"
4276
+ },
4277
+ homepage: "https://github.com/hasna/loops#readme",
4278
+ bugs: {
4279
+ url: "https://github.com/hasna/loops/issues"
4280
+ },
4281
+ author: "Hasna <andrei@hasna.com>",
4282
+ license: "Apache-2.0",
4283
+ engines: {
4284
+ bun: ">=1.0.0"
4285
+ },
4286
+ dependencies: {
4287
+ "@hasna/machines": "0.0.49",
4288
+ "@openrouter/ai-sdk-provider": "2.9.1",
4289
+ ai: "6.0.204",
4290
+ commander: "^13.1.0",
4291
+ zod: "4.4.3"
4292
+ },
4293
+ devDependencies: {
4294
+ "@types/bun": "latest",
4295
+ typescript: "^5.7.3"
4296
+ },
4297
+ publishConfig: {
4298
+ registry: "https://registry.npmjs.org",
4299
+ access: "public"
4300
+ }
4301
+ };
4302
+
4303
+ // src/lib/version.ts
4304
+ function packageVersion() {
4305
+ if (typeof package_default.version !== "string" || package_default.version.trim() === "")
4306
+ throw new Error("package.json version is missing");
4307
+ return package_default.version;
4308
+ }
4216
4309
 
4217
4310
  // src/cli/index.ts
4218
4311
  var program = new Command;
4219
- program.name("loops").description("Persistent local loops for commands and headless coding agents").version("0.3.6");
4312
+ program.name("loops").description("Persistent local loops for commands and headless coding agents").version(packageVersion());
4220
4313
  program.option("-j, --json", "print JSON");
4221
4314
  function isJson() {
4222
4315
  return Boolean(program.opts().json);
@@ -2848,7 +2848,8 @@ function planPrompt(spec) {
2848
2848
  const budget = spec.tokenBudget ? `Token budget: ${spec.tokenBudget}.` : "No explicit token budget.";
2849
2849
  return [
2850
2850
  "Create a flat DAG goal plan for this objective.",
2851
- "Each node must have a stable short key, a concrete objective, optional dependsOn keys, and optional priority.",
2851
+ "Each node must have a stable short key, a concrete objective, dependsOn keys, priority, and tokenBudget.",
2852
+ "Use dependsOn: [] when there are no dependencies, priority: 0 when no priority is needed, and tokenBudget: null when no per-node budget is needed.",
2852
2853
  "Prefer the smallest plan that can prove the explicit requirements.",
2853
2854
  budget,
2854
2855
  `Objective: ${spec.objective}`
@@ -2860,6 +2861,9 @@ function achievementPrompt(goal, nodes, evidence) {
2860
2861
  "Run an adversarial achievement audit.",
2861
2862
  "Completion is unproven until every explicit requirement is verified against evidence.",
2862
2863
  "Return achieved=false if evidence is missing, ambiguous, or only asserts completion.",
2864
+ "Return achieved=true when the evidence proves every explicit requirement; do not return achieved=false with an empty unmetRequirements array.",
2865
+ "Return status as complete, active, blocked, budgetLimited, cancelled, or null when no status change is appropriate.",
2866
+ "Return evidence and unmetRequirements as arrays, using [] when empty.",
2863
2867
  "adversarialReview must be non-empty and must describe the attempted falsification.",
2864
2868
  `Goal: ${goal.objective}`,
2865
2869
  `Nodes: ${nodes.map((node) => `${node.key}=${node.status}`).join(", ")}`,
@@ -2876,18 +2880,18 @@ var DEFAULT_MAX_TURNS = 10;
2876
2880
  var PlanNodeSchema = z.object({
2877
2881
  key: z.string().min(1).max(64).regex(/^[A-Za-z0-9_.-]+$/),
2878
2882
  objective: z.string().min(1),
2879
- dependsOn: z.array(z.string().min(1)).optional().default([]),
2880
- priority: z.number().int().optional().default(0),
2881
- tokenBudget: z.number().int().positive().optional()
2883
+ dependsOn: z.array(z.string().min(1)),
2884
+ priority: z.number().int(),
2885
+ tokenBudget: z.number().int().positive().nullable()
2882
2886
  });
2883
2887
  var PlanSchema = z.object({
2884
2888
  nodes: z.array(PlanNodeSchema).min(1)
2885
2889
  });
2886
2890
  var AchievementSchema = z.object({
2887
2891
  achieved: z.boolean(),
2888
- status: z.enum(["active", "blocked", "budgetLimited", "complete", "cancelled"]).optional(),
2889
- evidence: z.array(z.string()).optional().default([]),
2890
- unmetRequirements: z.array(z.string()).optional().default([]),
2892
+ status: z.enum(["active", "blocked", "budgetLimited", "complete", "cancelled"]).nullable(),
2893
+ evidence: z.array(z.string()),
2894
+ unmetRequirements: z.array(z.string()),
2891
2895
  adversarialReview: z.string().min(1)
2892
2896
  });
2893
2897
  function normalizeGoalSpec2(spec) {
@@ -2971,7 +2975,7 @@ async function planGoal(store, goal, spec, model, opts) {
2971
2975
  objective: node.objective,
2972
2976
  dependsOn: node.dependsOn ?? [],
2973
2977
  priority: node.priority ?? 0,
2974
- tokenBudget: node.tokenBudget,
2978
+ tokenBudget: node.tokenBudget ?? undefined,
2975
2979
  sequence: index
2976
2980
  }));
2977
2981
  assertAcyclicNodes(rawNodes.map((node) => ({ key: node.key, dependsOn: node.dependsOn })));
@@ -4013,10 +4017,99 @@ function enableStartup(result) {
4013
4017
  };
4014
4018
  });
4015
4019
  }
4020
+ // package.json
4021
+ var package_default = {
4022
+ name: "@hasna/loops",
4023
+ version: "0.3.9",
4024
+ description: "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
4025
+ type: "module",
4026
+ main: "dist/index.js",
4027
+ types: "dist/index.d.ts",
4028
+ bin: {
4029
+ loops: "dist/cli/index.js",
4030
+ "loops-daemon": "dist/daemon/index.js"
4031
+ },
4032
+ exports: {
4033
+ ".": {
4034
+ types: "./dist/index.d.ts",
4035
+ import: "./dist/index.js"
4036
+ },
4037
+ "./sdk": {
4038
+ types: "./dist/sdk/index.d.ts",
4039
+ import: "./dist/sdk/index.js"
4040
+ },
4041
+ "./storage": {
4042
+ types: "./dist/lib/store.d.ts",
4043
+ import: "./dist/lib/store.js"
4044
+ }
4045
+ },
4046
+ files: [
4047
+ "dist",
4048
+ "README.md",
4049
+ "docs",
4050
+ "LICENSE"
4051
+ ],
4052
+ scripts: {
4053
+ build: "rm -rf dist && bun build src/cli/index.ts src/daemon/index.ts src/index.ts src/sdk/index.ts src/lib/store.ts --outdir dist --target bun --packages external && chmod +x dist/cli/index.js dist/daemon/index.js && tsc -p tsconfig.build.json --emitDeclarationOnly --outDir dist",
4054
+ "build:bin": "bun build src/cli/index.ts --compile --outfile dist/loops",
4055
+ typecheck: "tsc --noEmit",
4056
+ test: "bun test",
4057
+ prepare: "bun run build",
4058
+ "dev:cli": "bun run src/cli/index.ts",
4059
+ "dev:daemon": "bun run src/daemon/index.ts",
4060
+ prepublishOnly: "bun run build"
4061
+ },
4062
+ keywords: [
4063
+ "loops",
4064
+ "scheduler",
4065
+ "daemon",
4066
+ "agents",
4067
+ "claude",
4068
+ "cursor",
4069
+ "codewith",
4070
+ "opencode",
4071
+ "cli"
4072
+ ],
4073
+ repository: {
4074
+ type: "git",
4075
+ url: "git+https://github.com/hasna/loops.git"
4076
+ },
4077
+ homepage: "https://github.com/hasna/loops#readme",
4078
+ bugs: {
4079
+ url: "https://github.com/hasna/loops/issues"
4080
+ },
4081
+ author: "Hasna <andrei@hasna.com>",
4082
+ license: "Apache-2.0",
4083
+ engines: {
4084
+ bun: ">=1.0.0"
4085
+ },
4086
+ dependencies: {
4087
+ "@hasna/machines": "0.0.49",
4088
+ "@openrouter/ai-sdk-provider": "2.9.1",
4089
+ ai: "6.0.204",
4090
+ commander: "^13.1.0",
4091
+ zod: "4.4.3"
4092
+ },
4093
+ devDependencies: {
4094
+ "@types/bun": "latest",
4095
+ typescript: "^5.7.3"
4096
+ },
4097
+ publishConfig: {
4098
+ registry: "https://registry.npmjs.org",
4099
+ access: "public"
4100
+ }
4101
+ };
4102
+
4103
+ // src/lib/version.ts
4104
+ function packageVersion() {
4105
+ if (typeof package_default.version !== "string" || package_default.version.trim() === "")
4106
+ throw new Error("package.json version is missing");
4107
+ return package_default.version;
4108
+ }
4016
4109
 
4017
4110
  // src/daemon/index.ts
4018
4111
  var program = new Command;
4019
- program.name("loops-daemon").description("OpenLoops daemon helper").version("0.3.6");
4112
+ program.name("loops-daemon").description("OpenLoops daemon helper").version(packageVersion());
4020
4113
  program.command("run").option("--interval-ms <ms>", "tick interval", (value) => Number(value)).action(async (opts) => runDaemon({ intervalMs: opts.intervalMs }));
4021
4114
  program.command("start").action(async () => {
4022
4115
  const result = await startDaemon({ cliEntry: process.argv[1] ?? "loops-daemon", args: ["run"] });
package/dist/index.js CHANGED
@@ -2838,7 +2838,8 @@ function planPrompt(spec) {
2838
2838
  const budget = spec.tokenBudget ? `Token budget: ${spec.tokenBudget}.` : "No explicit token budget.";
2839
2839
  return [
2840
2840
  "Create a flat DAG goal plan for this objective.",
2841
- "Each node must have a stable short key, a concrete objective, optional dependsOn keys, and optional priority.",
2841
+ "Each node must have a stable short key, a concrete objective, dependsOn keys, priority, and tokenBudget.",
2842
+ "Use dependsOn: [] when there are no dependencies, priority: 0 when no priority is needed, and tokenBudget: null when no per-node budget is needed.",
2842
2843
  "Prefer the smallest plan that can prove the explicit requirements.",
2843
2844
  budget,
2844
2845
  `Objective: ${spec.objective}`
@@ -2850,6 +2851,9 @@ function achievementPrompt(goal, nodes, evidence) {
2850
2851
  "Run an adversarial achievement audit.",
2851
2852
  "Completion is unproven until every explicit requirement is verified against evidence.",
2852
2853
  "Return achieved=false if evidence is missing, ambiguous, or only asserts completion.",
2854
+ "Return achieved=true when the evidence proves every explicit requirement; do not return achieved=false with an empty unmetRequirements array.",
2855
+ "Return status as complete, active, blocked, budgetLimited, cancelled, or null when no status change is appropriate.",
2856
+ "Return evidence and unmetRequirements as arrays, using [] when empty.",
2853
2857
  "adversarialReview must be non-empty and must describe the attempted falsification.",
2854
2858
  `Goal: ${goal.objective}`,
2855
2859
  `Nodes: ${nodes.map((node) => `${node.key}=${node.status}`).join(", ")}`,
@@ -2866,18 +2870,18 @@ var DEFAULT_MAX_TURNS = 10;
2866
2870
  var PlanNodeSchema = z.object({
2867
2871
  key: z.string().min(1).max(64).regex(/^[A-Za-z0-9_.-]+$/),
2868
2872
  objective: z.string().min(1),
2869
- dependsOn: z.array(z.string().min(1)).optional().default([]),
2870
- priority: z.number().int().optional().default(0),
2871
- tokenBudget: z.number().int().positive().optional()
2873
+ dependsOn: z.array(z.string().min(1)),
2874
+ priority: z.number().int(),
2875
+ tokenBudget: z.number().int().positive().nullable()
2872
2876
  });
2873
2877
  var PlanSchema = z.object({
2874
2878
  nodes: z.array(PlanNodeSchema).min(1)
2875
2879
  });
2876
2880
  var AchievementSchema = z.object({
2877
2881
  achieved: z.boolean(),
2878
- status: z.enum(["active", "blocked", "budgetLimited", "complete", "cancelled"]).optional(),
2879
- evidence: z.array(z.string()).optional().default([]),
2880
- unmetRequirements: z.array(z.string()).optional().default([]),
2882
+ status: z.enum(["active", "blocked", "budgetLimited", "complete", "cancelled"]).nullable(),
2883
+ evidence: z.array(z.string()),
2884
+ unmetRequirements: z.array(z.string()),
2881
2885
  adversarialReview: z.string().min(1)
2882
2886
  });
2883
2887
  function normalizeGoalSpec2(spec) {
@@ -2961,7 +2965,7 @@ async function planGoal(store, goal, spec, model, opts) {
2961
2965
  objective: node.objective,
2962
2966
  dependsOn: node.dependsOn ?? [],
2963
2967
  priority: node.priority ?? 0,
2964
- tokenBudget: node.tokenBudget,
2968
+ tokenBudget: node.tokenBudget ?? undefined,
2965
2969
  sequence: index
2966
2970
  }));
2967
2971
  assertAcyclicNodes(rawNodes.map((node) => ({ key: node.key, dependsOn: node.dependsOn })));
@@ -0,0 +1 @@
1
+ export declare function packageVersion(): string;
package/dist/sdk/index.js CHANGED
@@ -2838,7 +2838,8 @@ function planPrompt(spec) {
2838
2838
  const budget = spec.tokenBudget ? `Token budget: ${spec.tokenBudget}.` : "No explicit token budget.";
2839
2839
  return [
2840
2840
  "Create a flat DAG goal plan for this objective.",
2841
- "Each node must have a stable short key, a concrete objective, optional dependsOn keys, and optional priority.",
2841
+ "Each node must have a stable short key, a concrete objective, dependsOn keys, priority, and tokenBudget.",
2842
+ "Use dependsOn: [] when there are no dependencies, priority: 0 when no priority is needed, and tokenBudget: null when no per-node budget is needed.",
2842
2843
  "Prefer the smallest plan that can prove the explicit requirements.",
2843
2844
  budget,
2844
2845
  `Objective: ${spec.objective}`
@@ -2850,6 +2851,9 @@ function achievementPrompt(goal, nodes, evidence) {
2850
2851
  "Run an adversarial achievement audit.",
2851
2852
  "Completion is unproven until every explicit requirement is verified against evidence.",
2852
2853
  "Return achieved=false if evidence is missing, ambiguous, or only asserts completion.",
2854
+ "Return achieved=true when the evidence proves every explicit requirement; do not return achieved=false with an empty unmetRequirements array.",
2855
+ "Return status as complete, active, blocked, budgetLimited, cancelled, or null when no status change is appropriate.",
2856
+ "Return evidence and unmetRequirements as arrays, using [] when empty.",
2853
2857
  "adversarialReview must be non-empty and must describe the attempted falsification.",
2854
2858
  `Goal: ${goal.objective}`,
2855
2859
  `Nodes: ${nodes.map((node) => `${node.key}=${node.status}`).join(", ")}`,
@@ -2866,18 +2870,18 @@ var DEFAULT_MAX_TURNS = 10;
2866
2870
  var PlanNodeSchema = z.object({
2867
2871
  key: z.string().min(1).max(64).regex(/^[A-Za-z0-9_.-]+$/),
2868
2872
  objective: z.string().min(1),
2869
- dependsOn: z.array(z.string().min(1)).optional().default([]),
2870
- priority: z.number().int().optional().default(0),
2871
- tokenBudget: z.number().int().positive().optional()
2873
+ dependsOn: z.array(z.string().min(1)),
2874
+ priority: z.number().int(),
2875
+ tokenBudget: z.number().int().positive().nullable()
2872
2876
  });
2873
2877
  var PlanSchema = z.object({
2874
2878
  nodes: z.array(PlanNodeSchema).min(1)
2875
2879
  });
2876
2880
  var AchievementSchema = z.object({
2877
2881
  achieved: z.boolean(),
2878
- status: z.enum(["active", "blocked", "budgetLimited", "complete", "cancelled"]).optional(),
2879
- evidence: z.array(z.string()).optional().default([]),
2880
- unmetRequirements: z.array(z.string()).optional().default([]),
2882
+ status: z.enum(["active", "blocked", "budgetLimited", "complete", "cancelled"]).nullable(),
2883
+ evidence: z.array(z.string()),
2884
+ unmetRequirements: z.array(z.string()),
2881
2885
  adversarialReview: z.string().min(1)
2882
2886
  });
2883
2887
  function normalizeGoalSpec2(spec) {
@@ -2961,7 +2965,7 @@ async function planGoal(store, goal, spec, model, opts) {
2961
2965
  objective: node.objective,
2962
2966
  dependsOn: node.dependsOn ?? [],
2963
2967
  priority: node.priority ?? 0,
2964
- tokenBudget: node.tokenBudget,
2968
+ tokenBudget: node.tokenBudget ?? undefined,
2965
2969
  sequence: index
2966
2970
  }));
2967
2971
  assertAcyclicNodes(rawNodes.map((node) => ({ key: node.key, dependsOn: node.dependsOn })));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/loops",
3
- "version": "0.3.7",
3
+ "version": "0.3.9",
4
4
  "description": "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",