Skip to content

Agent confuses process.cwd() and project_root #8

Description

@ForestMars

Coding agent is attempting to work in process.cwd() rather than project_root which is kind of a recipe disaster. In theory, we should be able to point the agent at this ticket, for it to execute the changes. But it would still have to target a working copy of its own repo (ie project_root is a clone of https://github.com/ForestMars/Coda2) rather than doing surgery on the live running instance of itself, obviously.

As part of this ticket we also need to move the system prompt out of code and into its own file, as we do for support-agent.

Here's my proposed fix:

  1. Add PROJECT_ROOT resolver utility
    Create packages/lib/src/project-root.ts:
    typescriptimport { resolve, dirname } from "path";
    import { existsSync } from "fs";

/**

  • Resolves the working project directory for the agent.
  • Priority order:
    1. CODA_PROJECT_DIR env var (explicit override)
    1. --project CLI arg
    1. Walk up from process.cwd() until we find a .git or package.json
  •  that is NOT Coda2's own root
    

*/
export function resolveProjectRoot(): string {
// 1. Explicit env override
if (process.env.CODA_PROJECT_DIR) {
const dir = resolve(process.env.CODA_PROJECT_DIR);
if (!existsSync(dir)) {
throw new Error(CODA_PROJECT_DIR="${dir}" does not exist);
}
return dir;
}

// 2. CLI arg: --project /some/path
const projectArgIdx = process.argv.indexOf("--project");
if (projectArgIdx !== -1 && process.argv[projectArgIdx + 1]) {
return resolve(process.argv[projectArgIdx + 1]);
}

// 3. Walk up from CWD, skip our own repo root
const codaRoot = resolve(import.meta.dir, "../../.."); // packages/lib/src -> repo root
let dir = process.cwd();
while (dir !== dirname(dir)) {
if (dir !== codaRoot && existsSync(${dir}/.git)) {
return dir;
}
dir = dirname(dir);
}

// Fallback: use process.cwd() and warn loudly
console.warn(
[Coda] WARNING: Could not determine project root. +
Defaulting to process.cwd()="${process.cwd()}". +
Set CODA_PROJECT_DIR or pass --project <path>.
);
return process.cwd();
}

// Singleton — resolved once at startup
export const PROJECT_ROOT = resolveProjectRoot();
2. Fix your tools to use it
Wherever your tools execute shell commands or resolve file paths, swap process.cwd() for PROJECT_ROOT. In packages/tools, the pattern to hunt for is:
typescript// ❌ Before — anchors to wherever bun was launched
const result = await $${cmd}.cwd(process.cwd());
const absPath = path.resolve(filePath); // same problem

// ✅ After
import { PROJECT_ROOT } from "@coda/lib/project-root";
const result = await $${cmd}.cwd(PROJECT_ROOT);
const absPath = path.resolve(PROJECT_ROOT, filePath);
For child_process.spawn / execa style calls:
typescriptspawn(cmd, args, { cwd: PROJECT_ROOT }) // not process.cwd()
3. Inject it into the system prompt — prominently
This is the part that makes the LLM safe. In whatever builds your system prompt (likely in packages/agents), add this at the very top of the system content, before any other instructions:
typescriptimport { PROJECT_ROOT } from "@coda/lib/project-root";

const systemPrompt = [
// === PRIMACY POSITION — agent reads this first ===
{
type: "text",
text: `## Working Directory
Your current working project directory is:

${PROJECT_ROOT}

CRITICAL RULES:

  • ALL file paths you reference or create MUST be absolute paths under ${PROJECT_ROOT}
  • NEVER use paths outside of ${PROJECT_ROOT} unless explicitly instructed
  • NEVER assume the working directory is / or any other system root
  • When in doubt about a path, prefix it with ${PROJECT_ROOT}/
  • You are NOT working in the Coda agent's own source directory`,
    },
    // ...
    ];
    This follows the same "Context Primacy" pattern you already use for your Knowledge Graph in the SupportBot — inject the most important grounding fact first.
  1. Add it to .env for convenience
    bash# .env
    OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=http://otel-collector:4318/v1/logs
    OTEL_SERVICE_NAME=sup-agent
    LOG_LEVEL=info
    LOKI_HOST=http://localhost:3100

Set this to lock the agent to a specific project directory

CODA_PROJECT_DIR=/path/to/your/target/project

And update the npm script to make it easy to pass:
json"chat": "bun --env-file=.auth apps/cli/chat.ts",
"chat:project": "CODA_PROJECT_DIR=$PROJECT bun --env-file=.auth apps/cli/chat.ts",
So users can run: PROJECT=/my/app bun chat:project

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions