The shared module provides common utilities used across the orchestrator, including repository state inspection, LLM response parsing, and concurrency control.
Repository State
readRepoState()
Reads the current state of the target repository.
async function readRepoState(targetRepoPath: string): Promise<RepoState>
Path to the local clone of the target repository
interface RepoState {
fileTree: string[]; // Up to 300 files
recentCommits: string[]; // Last 40 commits
featuresJson: string | null; // FEATURES.json content (if exists)
specMd: string | null; // SPEC.md content (if exists)
agentsMd: string | null; // AGENTS.md content (if exists)
decisionsMd: string | null; // DECISIONS.md content (if exists)
}
File contents are truncated to prevent excessive token usage:
- File tree: max 300 entries
- FEATURES.json: max 20,000 chars
- SPEC.md: max 20,000 chars
- AGENTS.md: max 10,000 chars
- DECISIONS.md: max 10,000 chars
LLM Response Parsing
parsePlannerResponse()
Parses planner LLM output into structured format with salvage logic for truncated responses.
function parsePlannerResponse(content: string): PlannerResponseResult
Raw LLM response text (may be JSON, fenced JSON, or truncated)
interface PlannerResponseResult {
scratchpad: string; // Planner's working memory
tasks: RawTaskInput[]; // Tasks extracted from response
}
Features:
- Strips markdown code fences (
```json ... ```)
- Handles truncated responses by salvaging partial JSON
- Extracts scratchpad and tasks separately
- Falls back to task-array-only parsing if full parse fails
parseLLMTaskArray()
Parses a JSON array of tasks from LLM output.
function parseLLMTaskArray(content: string): RawTaskInput[]
Concurrency Control
ConcurrencyLimiter
Counting semaphore for bounding concurrent async operations.
class ConcurrencyLimiter {
constructor(maxConcurrent: number);
async acquire(): Promise<void>;
release(): void;
getActive(): number;
getQueueLength(): number;
}
Example:
const limiter = new ConcurrencyLimiter(5);
async function processTask(task: Task) {
await limiter.acquire();
try {
await executeTask(task);
} finally {
limiter.release();
}
}
// Process 100 tasks with max 5 concurrent
await Promise.all(tasks.map(processTask));
GitMutex
Serializes local git operations to prevent index.lock contention.
class GitMutex extends ConcurrencyLimiter {
constructor(); // Always maxConcurrent=1
isLocked(): boolean;
}
Example:
const gitMutex = new GitMutex();
async function mergeBranch(branch: string) {
await gitMutex.acquire();
try {
await git.merge(branch, "main", { strategy: "rebase" });
} finally {
gitMutex.release();
}
}
Always use GitMutex for any git operations that modify the working tree or index to avoid corruption.
Branch Utilities
slugifyForBranch()
Converts a task description into a git-safe branch slug.
function slugifyForBranch(description: string): string
Task description to slugify
Branch-safe slug (lowercase, alphanumeric + hyphens, max 50 chars)
Examples:
slugifyForBranch("Implement JWT token generation in src/auth")
// → "implement-jwt-token-generation-in-src-auth"
slugifyForBranch("Fix #123: Handle edge case in parser!!!")
// → "fix-123-handle-edge-case-in-parser"
slugifyForBranch("A".repeat(100))
// → "a".repeat(50) (truncated to 50 chars)
Pi Session Management
createPlannerPiSession()
Creates a read-only Pi coding agent session for the planner to explore the repository.
async function createPlannerPiSession(
options: PiSessionOptions
): Promise<PiSessionResult>
interface PiSessionOptions {
systemPrompt: string;
targetRepoPath: string;
llmConfig: LLMConfig;
}
interface PiSessionResult {
session: AgentSession;
tempDir: string; // Temporary directory for session files
}
Tools provided:
read — Read file contents
glob — Search for files by pattern
grep — Search file contents
bash (read-only git) — Run git commands (read-only subset)
The Pi session can only run read-only git commands: log, diff, show, status, branch, ls-files, etc. Mutating commands are blocked.
cleanupPiSession()
Cleans up a Pi session and its temporary directory.
function cleanupPiSession(session: AgentSession, tempDir: string): void
Creates a Bash tool restricted to read-only git commands.
function createGitReadOnlyBashTool(cwd: string): BashTool
Allowed git subcommands:
log, diff, show, status, branch
rev-parse, shortlog, blame, tag
ls-files, ls-tree, cat-file
name-rev, describe
Blocked:
- All non-git commands
add, commit, push, pull, merge, rebase, reset, etc.
Constants
export const MAX_HANDOFF_SUMMARY_CHARS = 300;
export const MAX_FILES_PER_HANDOFF = 30;
Helper Functions
sleep()
Promise-based sleep utility.
function sleep(ms: number): Promise<void>
Usage Example
import {
readRepoState,
parsePlannerResponse,
GitMutex,
slugifyForBranch,
createPlannerPiSession,
cleanupPiSession,
} from "@longshot/orchestrator";
// Read repository state
const repoState = await readRepoState("/path/to/repo");
console.log(`Files: ${repoState.fileTree.length}`);
console.log(`Commits: ${repoState.recentCommits.length}`);
// Parse LLM response
const response = await llmClient.complete(prompt);
const parsed = parsePlannerResponse(response.content);
console.log(`Scratchpad: ${parsed.scratchpad}`);
console.log(`Tasks: ${parsed.tasks.length}`);
// Create branch slug
const branchName = `worker/${slugifyForBranch(parsed.tasks[0].description)}`;
// Git operations with mutex
const gitMutex = new GitMutex();
await gitMutex.acquire();
try {
await git.createBranch(branchName, "main");
} finally {
gitMutex.release();
}
// Create Pi session for exploration
const { session, tempDir } = await createPlannerPiSession({
systemPrompt: promptTemplate,
targetRepoPath: "/path/to/repo",
llmConfig: config.llm,
});
try {
const result = await session.send("Read SPEC.md and list all features");
console.log(result.text);
} finally {
cleanupPiSession(session, tempDir);
}