squads-cli 0.6.0 → 0.6.2

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.js CHANGED
@@ -1655,6 +1655,72 @@ function buildContextFromSquad(squadName, squadContent, agentName) {
1655
1655
  return context;
1656
1656
  }
1657
1657
 
1658
+ // src/lib/cron.ts
1659
+ function cronMatches(cron, date) {
1660
+ const parts = cron.trim().split(/\s+/);
1661
+ if (parts.length < 5) return false;
1662
+ const fields = [
1663
+ { value: date.getMinutes(), field: parts[0], min: 0, max: 59 },
1664
+ { value: date.getHours(), field: parts[1], min: 0, max: 23 },
1665
+ { value: date.getDate(), field: parts[2], min: 1, max: 31 },
1666
+ { value: date.getMonth() + 1, field: parts[3], min: 1, max: 12 },
1667
+ { value: date.getDay(), field: parts[4], min: 0, max: 6 }
1668
+ ];
1669
+ return fields.every(
1670
+ ({ value, field, min, max }) => fieldMatches(field, value, min, max)
1671
+ );
1672
+ }
1673
+ function fieldMatches(field, value, min, max) {
1674
+ if (field.includes(",")) {
1675
+ return field.split(",").some((part) => fieldMatches(part.trim(), value, min, max));
1676
+ }
1677
+ if (field.includes("/")) {
1678
+ const [range, stepStr] = field.split("/");
1679
+ const step = parseInt(stepStr);
1680
+ if (isNaN(step) || step <= 0) return false;
1681
+ let start = min;
1682
+ let end = max;
1683
+ if (range !== "*") {
1684
+ if (range.includes("-")) {
1685
+ [start, end] = range.split("-").map(Number);
1686
+ } else {
1687
+ start = parseInt(range);
1688
+ }
1689
+ }
1690
+ if (value < start || value > end) return false;
1691
+ return (value - start) % step === 0;
1692
+ }
1693
+ if (field.includes("-")) {
1694
+ const [start, end] = field.split("-").map(Number);
1695
+ return value >= start && value <= end;
1696
+ }
1697
+ if (field === "*") return true;
1698
+ return parseInt(field) === value;
1699
+ }
1700
+ function getNextCronRun(cron, after = /* @__PURE__ */ new Date()) {
1701
+ const next = new Date(after);
1702
+ next.setSeconds(0, 0);
1703
+ next.setMinutes(next.getMinutes() + 1);
1704
+ const maxIterations = 60 * 48;
1705
+ for (let i = 0; i < maxIterations; i++) {
1706
+ if (cronMatches(cron, next)) return next;
1707
+ next.setMinutes(next.getMinutes() + 1);
1708
+ }
1709
+ const fallback = new Date(after);
1710
+ fallback.setDate(fallback.getDate() + 1);
1711
+ return fallback;
1712
+ }
1713
+ function parseCooldown(cooldown) {
1714
+ const match = cooldown.match(/^(\d+)\s*(m|min|minutes?|h|hours?|d|days?)$/i);
1715
+ if (!match) return 0;
1716
+ const value = parseInt(match[1]);
1717
+ const unit = match[2].toLowerCase();
1718
+ if (unit.startsWith("m")) return value * 60 * 1e3;
1719
+ if (unit.startsWith("h")) return value * 60 * 60 * 1e3;
1720
+ if (unit.startsWith("d")) return value * 24 * 60 * 60 * 1e3;
1721
+ return 0;
1722
+ }
1723
+
1658
1724
  // src/lib/llm-clis.ts
1659
1725
  import { execSync as execSync3 } from "child_process";
1660
1726
  function commandExists2(command) {
@@ -2021,8 +2087,26 @@ function getProviderDisplayName(provider) {
2021
2087
 
2022
2088
  // src/commands/run.ts
2023
2089
  import { homedir as homedir3 } from "os";
2090
+ var DEFAULT_BRIDGE_URL = "http://localhost:8088";
2091
+ var DEFAULT_LEARNINGS_LIMIT = 5;
2092
+ var DEFAULT_CONTEXT_TOKENS = 8e3;
2093
+ var DEFAULT_FALLBACK_CHARS = 2e3;
2094
+ var MAX_AGENT_BRIEFS = 3;
2095
+ var MAX_SQUAD_BRIEFS = 2;
2096
+ var MAX_LEARNINGS_CHARS = 1500;
2097
+ var MAX_LEAD_STATE_CHARS = 1e3;
2098
+ var EXECUTION_EVENT_TIMEOUT_MS = 5e3;
2099
+ var VERIFICATION_STATE_MAX_CHARS = 2e3;
2100
+ var VERIFICATION_EXEC_TIMEOUT_MS = 3e4;
2101
+ var DRYRUN_DEF_MAX_CHARS = 500;
2102
+ var DRYRUN_CONTEXT_MAX_CHARS = 800;
2103
+ var DEFAULT_SCHEDULED_COOLDOWN_MS = 6 * 60 * 60 * 1e3;
2104
+ var DEFAULT_TIMEOUT_MINUTES = 30;
2105
+ var SOFT_DEADLINE_RATIO = 0.7;
2106
+ var LOG_FILE_INIT_DELAY_MS = 500;
2107
+ var VERBOSE_COMMAND_MAX_CHARS = 50;
2024
2108
  async function registerContextWithBridge(ctx) {
2025
- const bridgeUrl = process.env.SQUADS_BRIDGE_URL || "http://localhost:8088";
2109
+ const bridgeUrl = process.env.SQUADS_BRIDGE_URL || DEFAULT_BRIDGE_URL;
2026
2110
  try {
2027
2111
  const response = await fetch(`${bridgeUrl}/api/context/register`, {
2028
2112
  method: "POST",
@@ -2044,7 +2128,7 @@ async function registerContextWithBridge(ctx) {
2044
2128
  }
2045
2129
  }
2046
2130
  async function checkPreflightGates(squad, agent) {
2047
- const bridgeUrl = process.env.SQUADS_BRIDGE_URL || "http://localhost:8088";
2131
+ const bridgeUrl = process.env.SQUADS_BRIDGE_URL || DEFAULT_BRIDGE_URL;
2048
2132
  try {
2049
2133
  const response = await fetch(`${bridgeUrl}/api/execution/preflight`, {
2050
2134
  method: "POST",
@@ -2059,8 +2143,8 @@ async function checkPreflightGates(squad, agent) {
2059
2143
  return { allowed: true, gates: {} };
2060
2144
  }
2061
2145
  }
2062
- async function fetchLearnings(squad, limit = 5) {
2063
- const bridgeUrl = process.env.SQUADS_BRIDGE_URL || "http://localhost:8088";
2146
+ async function fetchLearnings(squad, limit = DEFAULT_LEARNINGS_LIMIT) {
2147
+ const bridgeUrl = process.env.SQUADS_BRIDGE_URL || DEFAULT_BRIDGE_URL;
2064
2148
  try {
2065
2149
  const response = await fetch(
2066
2150
  `${bridgeUrl}/api/learnings/relevant?squad=${encodeURIComponent(squad)}&limit=${limit}`
@@ -2091,7 +2175,7 @@ function gatherSquadContext(squadName, agentName, options = {}) {
2091
2175
  const squadsDir = findSquadsDir();
2092
2176
  if (!squadsDir) return "";
2093
2177
  const memoryDir = findMemoryDir();
2094
- const maxTokens = options.maxTokens || 8e3;
2178
+ const maxTokens = options.maxTokens || DEFAULT_CONTEXT_TOKENS;
2095
2179
  const sections = [];
2096
2180
  let estimatedTokens = 0;
2097
2181
  const estimateTokens = (text) => Math.ceil(text.length / 4);
@@ -2109,7 +2193,7 @@ function gatherSquadContext(squadName, agentName, options = {}) {
2109
2193
  if (outputMatch) squadContext += outputMatch[0] + "\n";
2110
2194
  if (contextMatch) squadContext += contextMatch[0] + "\n";
2111
2195
  if (!squadContext && squadContent.length > 0) {
2112
- squadContext = squadContent.substring(0, 2e3);
2196
+ squadContext = squadContent.substring(0, DEFAULT_FALLBACK_CHARS);
2113
2197
  }
2114
2198
  if (squadContext) {
2115
2199
  const tokens = estimateTokens(squadContext);
@@ -2143,7 +2227,7 @@ ${stateContent.trim()}`);
2143
2227
  const briefsDir = join4(memoryDir, squadName, agentName, "briefs");
2144
2228
  if (existsSync4(briefsDir)) {
2145
2229
  try {
2146
- const briefFiles = readdirSync2(briefsDir).filter((f) => f.endsWith(".md")).slice(0, 3);
2230
+ const briefFiles = readdirSync2(briefsDir).filter((f) => f.endsWith(".md")).slice(0, MAX_AGENT_BRIEFS);
2147
2231
  for (const briefFile of briefFiles) {
2148
2232
  const briefPath = join4(briefsDir, briefFile);
2149
2233
  const briefContent = readFileSync3(briefPath, "utf-8");
@@ -2164,7 +2248,7 @@ ${briefContent.trim()}`);
2164
2248
  const squadBriefsDir = join4(memoryDir, squadName, "_briefs");
2165
2249
  if (existsSync4(squadBriefsDir)) {
2166
2250
  try {
2167
- const squadBriefs = readdirSync2(squadBriefsDir).filter((f) => f.endsWith(".md")).slice(0, 2);
2251
+ const squadBriefs = readdirSync2(squadBriefsDir).filter((f) => f.endsWith(".md")).slice(0, MAX_SQUAD_BRIEFS);
2168
2252
  for (const briefFile of squadBriefs) {
2169
2253
  const briefPath = join4(squadBriefsDir, briefFile);
2170
2254
  const briefContent = readFileSync3(briefPath, "utf-8");
@@ -2207,8 +2291,8 @@ ${briefingContent.trim()}`);
2207
2291
  try {
2208
2292
  let learningsContent = readFileSync3(learningsPath, "utf-8");
2209
2293
  if (learningsContent.trim()) {
2210
- if (learningsContent.length > 1500) {
2211
- learningsContent = learningsContent.slice(0, 1500) + "\n...(truncated)";
2294
+ if (learningsContent.length > MAX_LEARNINGS_CHARS) {
2295
+ learningsContent = learningsContent.slice(0, MAX_LEARNINGS_CHARS) + "\n...(truncated)";
2212
2296
  }
2213
2297
  const tokens = estimateTokens(learningsContent);
2214
2298
  if (estimatedTokens + tokens < maxTokens) {
@@ -2225,8 +2309,8 @@ ${learningsContent.trim()}`);
2225
2309
  try {
2226
2310
  let leadState = readFileSync3(leadStatePath, "utf-8");
2227
2311
  if (leadState.trim()) {
2228
- if (leadState.length > 1e3) {
2229
- leadState = leadState.slice(0, 1e3) + "\n...(truncated)";
2312
+ if (leadState.length > MAX_LEAD_STATE_CHARS) {
2313
+ leadState = leadState.slice(0, MAX_LEAD_STATE_CHARS) + "\n...(truncated)";
2230
2314
  }
2231
2315
  const tokens = estimateTokens(leadState);
2232
2316
  if (estimatedTokens + tokens < maxTokens) {
@@ -2518,6 +2602,10 @@ function parseAgentFrontmatter(agentPath) {
2518
2602
  if (retriesMatch) {
2519
2603
  result.max_retries = parseInt(retriesMatch[1], 10);
2520
2604
  }
2605
+ const cooldownMatch = yaml2.match(/cooldown:\s*["']?([^"'\n]+)["']?/);
2606
+ if (cooldownMatch) {
2607
+ result.cooldown = cooldownMatch[1].trim();
2608
+ }
2521
2609
  return result;
2522
2610
  }
2523
2611
  async function emitExecutionEvent(eventType, data) {
@@ -2537,7 +2625,7 @@ async function emitExecutionEvent(eventType, data) {
2537
2625
  ...data.error ? { error: data.error } : {}
2538
2626
  }
2539
2627
  }),
2540
- signal: AbortSignal.timeout(5e3)
2628
+ signal: AbortSignal.timeout(EXECUTION_EVENT_TIMEOUT_MS)
2541
2629
  });
2542
2630
  return;
2543
2631
  } catch {
@@ -2573,7 +2661,7 @@ async function verifyExecution(squadName, agentName, criteria, options = {}) {
2573
2661
  if (memDir) {
2574
2662
  const statePath = join4(memDir, squadName, agentName, "state.md");
2575
2663
  if (existsSync4(statePath)) {
2576
- stateContent = readFileSync3(statePath, "utf-8").slice(0, 2e3);
2664
+ stateContent = readFileSync3(statePath, "utf-8").slice(0, VERIFICATION_STATE_MAX_CHARS);
2577
2665
  }
2578
2666
  }
2579
2667
  let recentCommits = "";
@@ -2610,7 +2698,7 @@ FAIL: <brief reason>`;
2610
2698
  const escapedPrompt = verifyPrompt.replace(/'/g, "'\\''");
2611
2699
  const result = execSync11(
2612
2700
  `claude --print --model haiku -- '${escapedPrompt}'`,
2613
- { encoding: "utf-8", cwd: projectRoot, timeout: 3e4 }
2701
+ { encoding: "utf-8", cwd: projectRoot, timeout: VERIFICATION_EXEC_TIMEOUT_MS }
2614
2702
  ).trim();
2615
2703
  if (options.verbose) {
2616
2704
  writeLine(` ${colors.dim}Verification: ${result}${RESET}`);
@@ -2801,7 +2889,7 @@ async function runLeadMode(squad, squadsDir, options) {
2801
2889
  writeLine();
2802
2890
  return;
2803
2891
  }
2804
- const timeoutMins = options.timeout || 30;
2892
+ const timeoutMins = options.timeout || DEFAULT_TIMEOUT_MINUTES;
2805
2893
  const agentList = agentFiles.map((a) => `- ${a.name}: ${a.role}`).join("\n");
2806
2894
  const agentPaths = agentFiles.map((a) => `- ${a.name}: ${a.path}`).join("\n");
2807
2895
  const prompt2 = `You are the Lead of the ${squad.name} squad.
@@ -2921,7 +3009,7 @@ ${learnings.map((l) => `- ${l.content}`).join("\n")}
2921
3009
  const dryRunContext = gatherSquadContext(squadName, agentName, { verbose: options.verbose, agentPath });
2922
3010
  if (options.verbose) {
2923
3011
  writeLine(` ${colors.dim}Agent definition:${RESET}`);
2924
- writeLine(` ${colors.dim}${definition.slice(0, 500)}...${RESET}`);
3012
+ writeLine(` ${colors.dim}${definition.slice(0, DRYRUN_DEF_MAX_CHARS)}...${RESET}`);
2925
3013
  if (learnings.length > 0) {
2926
3014
  writeLine(` ${colors.dim}Learnings: ${learnings.length} from bridge${RESET}`);
2927
3015
  }
@@ -2929,7 +3017,7 @@ ${learnings.map((l) => `- ${l.content}`).join("\n")}
2929
3017
  const fullContext = `${dryRunContext}${learningContext}`;
2930
3018
  writeLine();
2931
3019
  writeLine(` ${colors.cyan}Context to inject (${Math.ceil(fullContext.length / 4)} tokens):${RESET}`);
2932
- writeLine(` ${colors.dim}${fullContext.slice(0, 800)}...${RESET}`);
3020
+ writeLine(` ${colors.dim}${fullContext.slice(0, DRYRUN_CONTEXT_MAX_CHARS)}...${RESET}`);
2933
3021
  }
2934
3022
  }
2935
3023
  return;
@@ -2982,8 +3070,9 @@ ${learnings.map((l) => `- ${l.content}`).join("\n")}
2982
3070
  const isScheduledRun = options.trigger === "scheduled" || options.trigger === "smart";
2983
3071
  const bridgeHasNoHistory = preflight.gates.cooldown?.elapsed_sec === null;
2984
3072
  if (isScheduledRun && (!preflight.gates.cooldown || bridgeHasNoHistory)) {
2985
- const defaultCooldownMs = 6 * 60 * 60 * 1e3;
2986
- const localCooldown = checkLocalCooldown(squadName, agentName, defaultCooldownMs);
3073
+ const frontmatterForCooldown = parseAgentFrontmatter(agentPath);
3074
+ const cooldownMs = frontmatterForCooldown.cooldown ? parseCooldown(frontmatterForCooldown.cooldown) || DEFAULT_SCHEDULED_COOLDOWN_MS : DEFAULT_SCHEDULED_COOLDOWN_MS;
3075
+ const localCooldown = checkLocalCooldown(squadName, agentName, cooldownMs);
2987
3076
  if (!localCooldown.ok) {
2988
3077
  spinner.stop();
2989
3078
  writeLine();
@@ -3013,7 +3102,7 @@ ${learnings.map((l) => `- ${l.content}`).join("\n")}
3013
3102
  ${approvalInstructions}
3014
3103
  ` : "";
3015
3104
  const squadContext = gatherSquadContext(squadName, agentName, { verbose: options.verbose, agentPath });
3016
- const timeoutMins = options.timeout || 30;
3105
+ const timeoutMins = options.timeout || DEFAULT_TIMEOUT_MINUTES;
3017
3106
  const prompt2 = `Execute the ${agentName} agent from squad ${squadName}.
3018
3107
 
3019
3108
  Read the agent definition at ${agentPath} and follow its instructions exactly.
@@ -3034,7 +3123,7 @@ ${squadContext}${learningContext}${approvalContext}
3034
3123
  TIME LIMIT: You have ${timeoutMins} minutes. Work efficiently:
3035
3124
  - Focus on the most important tasks first
3036
3125
  - If a task is taking too long, move on and note it for next run
3037
- - Aim to complete within ${Math.floor(timeoutMins * 0.7)} minutes
3126
+ - Aim to complete within ${Math.floor(timeoutMins * SOFT_DEADLINE_RATIO)} minutes
3038
3127
 
3039
3128
  After completion:
3040
3129
 
@@ -3348,7 +3437,7 @@ async function executeWithClaude(prompt2, options) {
3348
3437
  SQUADS_TASK_TYPE: execContext.taskType,
3349
3438
  SQUADS_TRIGGER: execContext.trigger,
3350
3439
  SQUADS_EXECUTION_ID: execContext.executionId,
3351
- BRIDGE_API: process.env.SQUADS_BRIDGE_URL || "http://localhost:8088",
3440
+ BRIDGE_API: process.env.SQUADS_BRIDGE_URL || DEFAULT_BRIDGE_URL,
3352
3441
  OTEL_RESOURCE_ATTRIBUTES: `squads.squad=${execContext.squad},squads.agent=${execContext.agent},squads.task_type=${execContext.taskType},squads.trigger=${execContext.trigger},squads.execution_id=${execContext.executionId}`,
3353
3442
  ...effort ? { CLAUDE_EFFORT: effort } : {},
3354
3443
  ...skills && skills.length > 0 ? { CLAUDE_SKILLS: skills.join(",") } : {}
@@ -3420,7 +3509,7 @@ async function executeWithClaude(prompt2, options) {
3420
3509
  SQUADS_TASK_TYPE: execContext.taskType,
3421
3510
  SQUADS_TRIGGER: execContext.trigger,
3422
3511
  SQUADS_EXECUTION_ID: execContext.executionId,
3423
- BRIDGE_API: process.env.SQUADS_BRIDGE_URL || "http://localhost:8088"
3512
+ BRIDGE_API: process.env.SQUADS_BRIDGE_URL || DEFAULT_BRIDGE_URL
3424
3513
  };
3425
3514
  const modelFlag2 = claudeModelAlias ? `--model ${claudeModelAlias}` : "";
3426
3515
  const watchBranchName = `agent/${squadName}/${agentName}-${timestamp2}`;
@@ -3434,7 +3523,7 @@ async function executeWithClaude(prompt2, options) {
3434
3523
  env: watchEnv
3435
3524
  });
3436
3525
  child2.unref();
3437
- await new Promise((resolve) => setTimeout(resolve, 500));
3526
+ await new Promise((resolve) => setTimeout(resolve, LOG_FILE_INIT_DELAY_MS));
3438
3527
  writeLine(` ${colors.dim}Tailing log (Ctrl+C to stop watching, agent continues)...${RESET}`);
3439
3528
  writeLine();
3440
3529
  const tail = spawn2("tail", ["-f", logFile2], { stdio: "inherit" });
@@ -3485,7 +3574,7 @@ async function executeWithClaude(prompt2, options) {
3485
3574
  SQUADS_TASK_TYPE: execContext.taskType,
3486
3575
  SQUADS_TRIGGER: execContext.trigger,
3487
3576
  SQUADS_EXECUTION_ID: execContext.executionId,
3488
- BRIDGE_API: process.env.SQUADS_BRIDGE_URL || "http://localhost:8088",
3577
+ BRIDGE_API: process.env.SQUADS_BRIDGE_URL || DEFAULT_BRIDGE_URL,
3489
3578
  OTEL_RESOURCE_ATTRIBUTES: `squads.squad=${execContext.squad},squads.agent=${execContext.agent},squads.task_type=${execContext.taskType},squads.trigger=${execContext.trigger},squads.execution_id=${execContext.executionId}`,
3490
3579
  ...effort ? { CLAUDE_EFFORT: effort } : {},
3491
3580
  ...skills && skills.length > 0 ? { CLAUDE_SKILLS: skills.join(",") } : {}
@@ -3538,7 +3627,7 @@ async function executeWithProvider(provider, prompt2, options) {
3538
3627
  }
3539
3628
  if (options.verbose) {
3540
3629
  writeLine(` ${colors.dim}Provider: ${cliConfig.displayName}${RESET}`);
3541
- writeLine(` ${colors.dim}Command: ${cliConfig.command} ${args.join(" ").slice(0, 50)}...${RESET}`);
3630
+ writeLine(` ${colors.dim}Command: ${cliConfig.command} ${args.join(" ").slice(0, VERBOSE_COMMAND_MAX_CHARS)}...${RESET}`);
3542
3631
  writeLine(` ${colors.dim}CWD: ${workDir}${RESET}`);
3543
3632
  if (workDir !== projectRoot) {
3544
3633
  writeLine(` ${colors.dim}Worktree: ${branchName}${RESET}`);
@@ -10018,74 +10107,6 @@ import {
10018
10107
  import { join as join17 } from "path";
10019
10108
  import { homedir as homedir6 } from "os";
10020
10109
  import { spawn as spawn3, execSync as execSync9 } from "child_process";
10021
-
10022
- // src/lib/cron.ts
10023
- function cronMatches(cron, date) {
10024
- const parts = cron.trim().split(/\s+/);
10025
- if (parts.length < 5) return false;
10026
- const fields = [
10027
- { value: date.getMinutes(), field: parts[0], min: 0, max: 59 },
10028
- { value: date.getHours(), field: parts[1], min: 0, max: 23 },
10029
- { value: date.getDate(), field: parts[2], min: 1, max: 31 },
10030
- { value: date.getMonth() + 1, field: parts[3], min: 1, max: 12 },
10031
- { value: date.getDay(), field: parts[4], min: 0, max: 6 }
10032
- ];
10033
- return fields.every(
10034
- ({ value, field, min, max }) => fieldMatches(field, value, min, max)
10035
- );
10036
- }
10037
- function fieldMatches(field, value, min, max) {
10038
- if (field.includes(",")) {
10039
- return field.split(",").some((part) => fieldMatches(part.trim(), value, min, max));
10040
- }
10041
- if (field.includes("/")) {
10042
- const [range, stepStr] = field.split("/");
10043
- const step = parseInt(stepStr);
10044
- if (isNaN(step) || step <= 0) return false;
10045
- let start = min;
10046
- let end = max;
10047
- if (range !== "*") {
10048
- if (range.includes("-")) {
10049
- [start, end] = range.split("-").map(Number);
10050
- } else {
10051
- start = parseInt(range);
10052
- }
10053
- }
10054
- if (value < start || value > end) return false;
10055
- return (value - start) % step === 0;
10056
- }
10057
- if (field.includes("-")) {
10058
- const [start, end] = field.split("-").map(Number);
10059
- return value >= start && value <= end;
10060
- }
10061
- if (field === "*") return true;
10062
- return parseInt(field) === value;
10063
- }
10064
- function getNextCronRun(cron, after = /* @__PURE__ */ new Date()) {
10065
- const next = new Date(after);
10066
- next.setSeconds(0, 0);
10067
- next.setMinutes(next.getMinutes() + 1);
10068
- const maxIterations = 60 * 48;
10069
- for (let i = 0; i < maxIterations; i++) {
10070
- if (cronMatches(cron, next)) return next;
10071
- next.setMinutes(next.getMinutes() + 1);
10072
- }
10073
- const fallback = new Date(after);
10074
- fallback.setDate(fallback.getDate() + 1);
10075
- return fallback;
10076
- }
10077
- function parseCooldown(cooldown) {
10078
- const match = cooldown.match(/^(\d+)\s*(m|min|minutes?|h|hours?|d|days?)$/i);
10079
- if (!match) return 0;
10080
- const value = parseInt(match[1]);
10081
- const unit = match[2].toLowerCase();
10082
- if (unit.startsWith("m")) return value * 60 * 1e3;
10083
- if (unit.startsWith("h")) return value * 60 * 60 * 1e3;
10084
- if (unit.startsWith("d")) return value * 24 * 60 * 60 * 1e3;
10085
- return 0;
10086
- }
10087
-
10088
- // src/commands/autonomous.ts
10089
10110
  var DAEMON_DIR = join17(homedir6(), ".squads");
10090
10111
  var PID_FILE = join17(DAEMON_DIR, "autonomous.pid");
10091
10112
  var DAEMON_LOG = join17(DAEMON_DIR, "autonomous.log");