export interface ProjectMetadata {
gameId: string;
engineVersion: string;
genre: string;
template: string;
/** See the same field in scaffold/project-files.ts. Absent on projects scaffolded before it. */
agentsMdHash?: string;
/**
* Which api-server this project belongs to. Absent on projects scaffolded before the field
* existed, which is why nothing may require it.
*
* Typed `string` rather than `EnvironmentName` on purpose, and NOT validated by
* findMissingField: this file is hand-editable, so a narrow type here would be a claim the
* parser cannot back, and the only honest way to keep it would be a cast. Validation against the
* environment table belongs to config/environments.ts — the same split credentials.ts already
* documents for `defaultEnvironment`, and the reason this module must not import that table.
*/
environment?: string;
/**
* Which agent tools this project is set up for. Absent on projects scaffolded before the field
* existed, which reads as the always-present set — see scaffold/agent-targets.ts.
*
* Typed `string[]` rather than `AgentId[]` for the same reason `environment` is typed `string`:
* this file is hand-editable, so a narrow type here would be a claim the parser cannot back.
* `readRecordedAgents` does the narrowing, and reports what it dropped.
*/
agents?: string[];
}
export interface ProjectContext {
root: string;
metadata: ProjectMetadata;
}
/**
* The nearest ancestor of `startDir` (itself included) holding `fileName`, or null at the root.
*
* `startDir` does NOT have to exist. `fs.existsSync` on a path under a missing directory is simply
* false, so the walk falls through to the nearest ancestor that does exist — which is what lets
* `bitmagic init
` ask about a target directory it has not created yet.
*
* Shared rather than written twice: the loop is four lines and two of them are easy to get wrong
* (resolving `startDir` on the way in, and stopping when `path.dirname` stops moving), so a second
* copy would drift. Config markers live above this module — see config/folder-environment.ts.
*/
export declare function findDirWithFileUpwards(fileName: string, startDir?: string): string | null;
/**
* Like findProjectRoot, but answers "is there one?" instead of insisting there is.
*
* Environment resolution runs on EVERY command, including the ones that legitimately have no
* project — `login`, `logout`, `whoami`, `usage`, `subscribe`, and `init`, which creates one.
*/
export declare function findProjectRootOrNull(startDir?: string): string | null;
/** Walks up from `startDir` looking for bitmagic.json, so commands work from any subdirectory. */
export declare function findProjectRoot(startDir?: string): string;
export declare function loadProjectContext(startDir?: string): ProjectContext;
/**
* The environment this project is pinned to, or undefined when there is no project — and for every
* project scaffolded before the field existed. "No project" is an answer here, not an error.
*
* Deliberately does NOT run findMissingField. `login` and `whoami` do not operate on the project,
* and refusing to log in because a bitmagic.json two directories up is missing `genre` would be a
* brand new failure for commands that never needed a project at all. A marker that cannot be
* PARSED still throws: that is not a "no project" answer, and swallowing it would point the user
* at `bitmagic init` when their file is merely malformed.
*
* Returns the raw string. Checking it against the environment table is config/environments.ts's
* job — see the note on ProjectMetadata.environment.
*/
export declare function readProjectEnvironment(startDir?: string): string | undefined;
/**
* The project's game id, or undefined when there is no project here or its marker does not name
* one — for the command beacon, which reports which game a command was run against.
*
* Never throws, unlike `readProjectEnvironment` and `loadProjectContext`. Those are read by
* commands doing their work, where a malformed marker must surface; this is read by telemetry
* after the command has already finished, where the only honest answer to a broken file is to
* say nothing about the game. A beacon is never a reason to fail a command that worked.
*/
export declare function readProjectGameIdOrNull(startDir?: string): string | undefined;
/**
* Resolves a tool from the PROJECT's node_modules, never the CLI's. The scaffold pins its own
* typescript and vite; running a different copy would type-check against the wrong compiler.
*
* Windows gets `.cmd` (or a real `.exe`) rather than the extensionless file, which is
* a POSIX shell script there and spawns ENOENT. Run whatever this returns through
* `spawn-bin.ts` — a `.cmd` additionally needs a shell, and that is the one place that knows it.
*/
export declare function projectBin(root: string, name: string, platform?: string): string;