orcastrator 0.2.13 → 0.2.14

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/README.md CHANGED
@@ -188,7 +188,7 @@ Global:
188
188
 
189
189
  - `--anthropic-key <key>`
190
190
  - `--openai-key <key>`
191
- - `--check` (API key lookup order: CLI flag → process env → `~/.openclaw/openclaw.json` `env.vars` → `~/.claude/.env` → `~/.config/claude/.env` → `./.env`)
191
+ - `--check` (API key lookup order: CLI flag → process env → `~/.openclaw/openclaw.json` `env.vars` → `~/.claude/.env` → `~/.config/claude/.env`)
192
192
  - `--global`
193
193
  - `--project`
194
194
 
@@ -57,7 +57,7 @@ export function resolveApiKey(flagValue, envVarName, openclawConfigPathOrOptions
57
57
  return envValue.trim();
58
58
  }
59
59
  const options = typeof openclawConfigPathOrOptions === "string"
60
- ? { ...(maybeOptions ?? {}), openclawConfigPath: openclawConfigPathOrOptions }
60
+ ? { ...maybeOptions, openclawConfigPath: openclawConfigPathOrOptions }
61
61
  : (openclawConfigPathOrOptions ?? {});
62
62
  const homedir = options.homedir ?? os.homedir();
63
63
  const openclawValue = readOpenclawEnvVar(envVarName, options.openclawConfigPath, homedir);
@@ -65,7 +65,6 @@ export function resolveApiKey(flagValue, envVarName, openclawConfigPathOrOptions
65
65
  return openclawValue;
66
66
  }
67
67
  const dotenvValue = readDotEnvFallback(envVarName, {
68
- cwd: options.cwd ?? process.cwd(),
69
68
  homedir
70
69
  });
71
70
  if (dotenvValue) {
@@ -104,8 +103,7 @@ function readOpenclawEnvVar(envVarName, openclawConfigPath, homedir = os.homedir
104
103
  function readDotEnvFallback(envVarName, options) {
105
104
  const candidatePaths = [
106
105
  path.join(options.homedir, ".claude", ".env"),
107
- path.join(options.homedir, ".config", "claude", ".env"),
108
- path.join(options.cwd, ".env")
106
+ path.join(options.homedir, ".config", "claude", ".env")
109
107
  ];
110
108
  for (const candidatePath of candidatePaths) {
111
109
  const value = readEnvVarFromDotEnvFile(candidatePath, envVarName);
@@ -1,10 +1,13 @@
1
1
  import { promises as fs } from "node:fs";
2
+ import path from "node:path";
2
3
  import { planSpec as planSpecWithClaude } from "../agents/claude/session.js";
3
4
  import { planSpec as planSpecWithCodex } from "../agents/codex/session.js";
4
5
  import { logger } from "../utils/logger.js";
5
6
  import { loadSkills } from "../utils/skill-loader.js";
6
7
  import { validateDAG } from "./dependency-graph.js";
7
8
  const DEFAULT_SYSTEM_CONTEXT = "You are Orca planner.";
9
+ const PROJECT_INSTRUCTION_FILES = ["AGENTS.md", "CLAUDE.md"];
10
+ const PROJECT_INSTRUCTION_CHAR_CAP = 4_000;
8
11
  let testPlanSpecOverride = null;
9
12
  export function setPlanSpecForTests(fn) {
10
13
  testPlanSpecOverride = fn;
@@ -26,12 +29,77 @@ function formatSkillsSection(skills) {
26
29
  ].join("\n"));
27
30
  return ["## Available Skills", "", ...formattedSkills].join("\n");
28
31
  }
32
+ async function pathExists(targetPath) {
33
+ try {
34
+ await fs.access(targetPath);
35
+ return true;
36
+ }
37
+ catch {
38
+ return false;
39
+ }
40
+ }
41
+ async function resolveProjectContextDir(specPath) {
42
+ let currentDir = path.dirname(path.resolve(specPath));
43
+ while (true) {
44
+ const gitMarker = path.join(currentDir, ".git");
45
+ if (await pathExists(gitMarker)) {
46
+ return currentDir;
47
+ }
48
+ const parent = path.dirname(currentDir);
49
+ if (parent === currentDir) {
50
+ return path.dirname(path.resolve(specPath));
51
+ }
52
+ currentDir = parent;
53
+ }
54
+ }
55
+ async function loadProjectInstructions(specPath) {
56
+ const projectDir = await resolveProjectContextDir(specPath);
57
+ const instructions = [];
58
+ for (const fileName of PROJECT_INSTRUCTION_FILES) {
59
+ const filePath = path.join(projectDir, fileName);
60
+ if (!(await pathExists(filePath))) {
61
+ continue;
62
+ }
63
+ const rawContent = await fs.readFile(filePath, "utf8");
64
+ const content = rawContent.slice(0, PROJECT_INSTRUCTION_CHAR_CAP);
65
+ instructions.push({
66
+ fileName,
67
+ filePath,
68
+ content,
69
+ truncated: rawContent.length > PROJECT_INSTRUCTION_CHAR_CAP
70
+ });
71
+ }
72
+ return instructions;
73
+ }
74
+ function formatProjectInstructionsSection(instructions) {
75
+ const parts = ["## Project Instructions"];
76
+ for (const instruction of instructions) {
77
+ parts.push("");
78
+ parts.push(`### ${instruction.fileName} (${instruction.filePath})`);
79
+ parts.push("");
80
+ parts.push("```md");
81
+ parts.push(instruction.content);
82
+ parts.push("```");
83
+ if (instruction.truncated) {
84
+ parts.push(`(truncated to ${PROJECT_INSTRUCTION_CHAR_CAP} characters)`);
85
+ }
86
+ }
87
+ return parts.join("\n");
88
+ }
89
+ function buildSystemContext(skills, instructions) {
90
+ const sections = [DEFAULT_SYSTEM_CONTEXT];
91
+ if (instructions.length > 0) {
92
+ sections.push(formatProjectInstructionsSection(instructions));
93
+ }
94
+ if (skills.length > 0) {
95
+ sections.push(formatSkillsSection(skills));
96
+ }
97
+ return sections.join("\n\n");
98
+ }
29
99
  export async function runPlanner(specPath, store, runId, config) {
30
100
  const spec = await fs.readFile(specPath, "utf8");
31
- const skills = await loadSkills(config);
32
- const systemContext = skills.length === 0
33
- ? DEFAULT_SYSTEM_CONTEXT
34
- : `${DEFAULT_SYSTEM_CONTEXT}\n\n${formatSkillsSection(skills)}`;
101
+ const [skills, instructions] = await Promise.all([loadSkills(config), loadProjectInstructions(specPath)]);
102
+ const systemContext = buildSystemContext(skills, instructions);
35
103
  const planSpecImpl = resolvePlanSpecImpl(config);
36
104
  const result = await planSpecImpl(spec, systemContext, config);
37
105
  validateDAG(result.tasks);
@@ -1,4 +1,4 @@
1
- export function createStdoutHookHandler(prefix = "[hook]") {
1
+ export function createStdoutHookHandler(prefix = "[hook]", write = console.log) {
2
2
  return async (event) => {
3
3
  const line = {
4
4
  prefix,
@@ -10,6 +10,6 @@ export function createStdoutHookHandler(prefix = "[hook]") {
10
10
  error: event.error,
11
11
  metadata: event.metadata
12
12
  };
13
- console.log(JSON.stringify(line));
13
+ write(JSON.stringify(line));
14
14
  };
15
15
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orcastrator",
3
- "version": "0.2.13",
3
+ "version": "0.2.14",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "orca": "dist/cli/index.js"
@@ -10,7 +10,7 @@
10
10
  "dev": "bun run src/cli/index.ts",
11
11
  "lint": "oxlint src/",
12
12
  "typecheck": "tsc --noEmit",
13
- "test": "bun test",
13
+ "test": "bun test src",
14
14
  "start": "bun run src/cli/index.ts",
15
15
  "prepare": "husky",
16
16
  "postbuild": "chmod +x dist/cli/index.js"
@@ -21,7 +21,7 @@
21
21
  "@ratley/codex-client": "^0.1.3",
22
22
  "chalk": "^5.3.0",
23
23
  "commander": "^13.1.0",
24
- "zod": "^3.24.1"
24
+ "zod": "^4.3.6"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@types/bun": "^1.2.21",