Skip to main content
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>
targetRepoPath
string
required
Path to the local clone of the target repository
RepoState
object
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
content
string
required
Raw LLM response text (may be JSON, fenced JSON, or truncated)
return
PlannerResponseResult
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
description
string
required
Task description to slugify
return
string
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

createGitReadOnlyBashTool()

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);
}

Build docs developers (and LLMs) love