@primitive.ai/prim 0.1.0-alpha.74 → 0.1.0-alpha.75

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.
@@ -20473,6 +20473,23 @@ function decisionIngestionStatus(cwd) {
20473
20473
  return isRepoActiveForCapture(cwd) ? "enabled" : "disabled";
20474
20474
  }
20475
20475
 
20476
+ // src/lib/confirmation.ts
20477
+ function isNonInteractive(globals) {
20478
+ return Boolean(globals.nonInteractive || process.env.CI || process.env.PRIM_NON_INTERACTIVE);
20479
+ }
20480
+ async function askConfirmation(question, output = process.stdout) {
20481
+ if (!process.stdin.isTTY) return false;
20482
+ const { createInterface } = await import("readline/promises");
20483
+ const rl = createInterface({ input: process.stdin, output });
20484
+ try {
20485
+ const answer = await rl.question(`${question} [y/N] `);
20486
+ const normalized = answer.trim().toLowerCase();
20487
+ return normalized === "y" || normalized === "yes";
20488
+ } finally {
20489
+ rl.close();
20490
+ }
20491
+ }
20492
+
20476
20493
  // src/lib/post-commit-hook.ts
20477
20494
  import { execFileSync as execFileSync3 } from "child_process";
20478
20495
  import { createHash as createHash4, randomUUID as randomUUID2 } from "crypto";
@@ -22353,85 +22370,72 @@ function printJson(data) {
22353
22370
  console.log(JSON.stringify(data, null, 2));
22354
22371
  }
22355
22372
 
22356
- // src/commands/activation.ts
22357
- async function applyActivation(active) {
22358
- const root = gitToplevel();
22359
- if (!root) {
22360
- process.stderr.write(
22361
- `[prim] not a git repository \u2014 run \`prim ${active ? "enable" : "disable"}\` inside a repo
22362
- `
22363
- );
22364
- process.exit(1);
22373
+ // src/lib/ansi.ts
22374
+ var ANSI_CODES = {
22375
+ yellow: "\x1B[33m",
22376
+ magenta: "\x1B[35m",
22377
+ blue: "\x1B[34m",
22378
+ cyan: "\x1B[36m",
22379
+ green: "\x1B[32m",
22380
+ red: "\x1B[31m",
22381
+ // 256-color approximation of the orange trigger marker (xterm 208).
22382
+ orange: "\x1B[38;5;208m",
22383
+ gray: "\x1B[90m"
22384
+ };
22385
+ var ANSI_RESET = "\x1B[0m";
22386
+ var ANSI_DIM = "\x1B[2m";
22387
+ var ANSI_BOLD = "\x1B[1m";
22388
+ function supportsColor2() {
22389
+ if (process.env.NO_COLOR !== void 0 && process.env.NO_COLOR !== "") {
22390
+ return false;
22365
22391
  }
22366
- let phase = active ? "post-commit hook coverage" : "local deactivation";
22367
- try {
22368
- let binding;
22369
- let postCommitHook;
22370
- let postRewriteHook;
22371
- if (active) {
22372
- postCommitHook = ensureEffectivePostCommitHook(root).path;
22373
- try {
22374
- postRewriteHook = ensureEffectivePostRewriteHook(root).path;
22375
- } catch (error) {
22376
- const detail = error instanceof Error ? error.message : String(error);
22377
- process.stderr.write(`[prim] post-rewrite hook coverage is degraded: ${detail}
22378
- `);
22379
- }
22380
- phase = "repository binding";
22381
- binding = await bindRepository(root);
22382
- phase = "local activation";
22383
- }
22384
- setRepoActive(root, active);
22385
- await daemonRequest("statusline_invalidate", {}, { timeoutMs: 250 });
22386
- if (binding?.status === "unbound") {
22387
- process.stderr.write(`[prim] Prim is enabled locally in ${root}
22388
- `);
22389
- process.stderr.write(
22390
- "[prim] repository is not connected to Primitive; Moves still ingest into the team graph without repository-specific file attribution, Conflict Gate verification, or commit correlation\n"
22391
- );
22392
- process.stderr.write(
22393
- `[prim] ask an organization owner or administrator to grant Primitive's GitHub App access to this repository through Primitive's GitHub App onboarding; binding retries automatically at the next agent SessionStart
22394
- `
22395
- );
22396
- } else {
22397
- process.stderr.write(`[prim] prim ${active ? "enabled" : "disabled"} in ${root}
22398
- `);
22399
- }
22400
- printJson({
22401
- active,
22402
- repo: root,
22403
- ...binding ? {
22404
- bindingStatus: binding.status,
22405
- repositoryFullName: binding.repositoryFullName,
22406
- ...binding.status === "connected" ? { repoSyncId: binding.repoSyncId } : {}
22407
- } : {},
22408
- ...postCommitHook ? { postCommitHook } : {},
22409
- ...postRewriteHook ? { postRewriteHook } : {}
22410
- });
22411
- } catch (err) {
22412
- const detail = err instanceof Error ? err.message : String(err);
22413
- process.stderr.write(
22414
- active ? `[prim] failed to enable prim during ${phase}: ${detail}
22415
- ` : `[prim] failed to disable prim: ${detail}
22416
- `
22417
- );
22418
- process.exit(1);
22392
+ return process.stderr.isTTY === true;
22393
+ }
22394
+ function color(text, c) {
22395
+ if (!supportsColor2()) {
22396
+ return text;
22419
22397
  }
22398
+ return `${ANSI_CODES[c]}${text}${ANSI_RESET}`;
22420
22399
  }
22421
- function registerActivationCommands(program3) {
22422
- program3.command("enable").description("Activate prim's hooks in this repo (git config prim.active=true)").action(() => applyActivation(true));
22423
- program3.command("disable").description("Mute prim's hooks in this repo (git config prim.active=false)").action(() => applyActivation(false));
22400
+ function dim(text) {
22401
+ if (!supportsColor2()) {
22402
+ return text;
22403
+ }
22404
+ return `${ANSI_DIM}${text}${ANSI_RESET}`;
22405
+ }
22406
+ function bold(text) {
22407
+ if (!supportsColor2()) {
22408
+ return text;
22409
+ }
22410
+ return `${ANSI_BOLD}${text}${ANSI_RESET}`;
22411
+ }
22412
+ var AREA_COLORS = {
22413
+ auth: "yellow",
22414
+ data: "magenta",
22415
+ mobile: "blue",
22416
+ infra: "cyan",
22417
+ ui: "green",
22418
+ billing: "orange",
22419
+ api: "blue",
22420
+ docs: "gray",
22421
+ testing: "gray"
22422
+ };
22423
+ function colorForArea(area) {
22424
+ if (!area) {
22425
+ return "gray";
22426
+ }
22427
+ return AREA_COLORS[area] ?? "gray";
22428
+ }
22429
+ var MAX_HEALTH_ERROR_LENGTH = 240;
22430
+ function boundedHealthError(value) {
22431
+ if (!value) return void 0;
22432
+ const clean = terminalSafeLine(value);
22433
+ if (!clean) return void 0;
22434
+ return clean.length <= MAX_HEALTH_ERROR_LENGTH ? clean : `${clean.slice(0, MAX_HEALTH_ERROR_LENGTH - 1)}\u2026`;
22435
+ }
22436
+ function stripAnsi2(text) {
22437
+ return text.replace(/\u001b\[[0-9;]*m/g, "");
22424
22438
  }
22425
-
22426
- // src/commands/auth.ts
22427
- import { execFile } from "child_process";
22428
- import { createHash as createHash6, randomBytes as randomBytes3 } from "crypto";
22429
- import { existsSync as existsSync6, readFileSync as readFileSync7 } from "fs";
22430
- import { createServer } from "http";
22431
- import { platform } from "os";
22432
-
22433
- // src/auth/api-keys.ts
22434
- import { randomBytes as randomBytes2 } from "crypto";
22435
22439
 
22436
22440
  // src/generated/cli-http-v1.validators.js
22437
22441
  var import_ucs2length = __toESM(require_ucs2length(), 1);
@@ -32165,7 +32169,113 @@ function isGitHubInstallIntentStatusResponse(value) {
32165
32169
  }
32166
32170
  }
32167
32171
 
32172
+ // src/lib/github-install-intent.ts
32173
+ var PROTOCOL_VERSION = 1;
32174
+ var MODE = "install_intent_v1";
32175
+ var START_PATH = "/api/cli/github/install-intents";
32176
+ var POLL_REQUEST_TIMEOUT_MS2 = 1e4;
32177
+ var MAX_INTENT_LIFETIME_MS = 15 * 6e4;
32178
+ var REPOSITORY_IDENTITY_MIGRATION_REQUIRED = "repository_identity_migration_required";
32179
+ function isRepositoryIdentityMigrationRequiredStatus(value) {
32180
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
32181
+ return false;
32182
+ }
32183
+ const record2 = value;
32184
+ if (record2.failureCode !== REPOSITORY_IDENTITY_MIGRATION_REQUIRED) {
32185
+ return false;
32186
+ }
32187
+ return isGitHubInstallIntentStatusResponse({
32188
+ ...record2,
32189
+ failureCode: "proof_commit_failed"
32190
+ });
32191
+ }
32192
+ function parseGitHubInstallIntentStart(value, now = Date.now()) {
32193
+ if (!isGitHubInstallIntentStartResponse(value) || value.expiresAt <= now || value.expiresAt - now > MAX_INTENT_LIFETIME_MS) {
32194
+ return null;
32195
+ }
32196
+ return value;
32197
+ }
32198
+ function parseGitHubInstallIntentStatus(value, expectedExpiresAt) {
32199
+ if (isGitHubInstallIntentStatusResponse(value)) {
32200
+ return value.expiresAt === expectedExpiresAt ? value : null;
32201
+ }
32202
+ if (!isRepositoryIdentityMigrationRequiredStatus(value) || value.expiresAt !== expectedExpiresAt) {
32203
+ return null;
32204
+ }
32205
+ return value;
32206
+ }
32207
+ async function createGitHubInstallIntent(client, options = {}) {
32208
+ const { now = Date.now, ...requestOptions } = options;
32209
+ const raw = await client.post(START_PATH, void 0, requestOptions);
32210
+ const parsed = parseGitHubInstallIntentStart(raw, now());
32211
+ if (!parsed) throw new Error("server returned an invalid GitHub install-intent response");
32212
+ return parsed;
32213
+ }
32214
+ function defaultSleep3(milliseconds, signal) {
32215
+ if (signal.aborted) return Promise.reject(signal.reason);
32216
+ return new Promise((resolvePromise, reject) => {
32217
+ const onAbort = () => {
32218
+ clearTimeout(timer);
32219
+ reject(signal.reason);
32220
+ };
32221
+ const timer = setTimeout(() => {
32222
+ signal.removeEventListener("abort", onAbort);
32223
+ resolvePromise();
32224
+ }, milliseconds);
32225
+ signal.addEventListener("abort", onAbort, { once: true });
32226
+ });
32227
+ }
32228
+ function expiredStatus(start) {
32229
+ return {
32230
+ protocolVersion: PROTOCOL_VERSION,
32231
+ mode: MODE,
32232
+ found: true,
32233
+ status: "expired",
32234
+ expiresAt: start.expiresAt,
32235
+ closedAt: start.expiresAt
32236
+ };
32237
+ }
32238
+ async function pollGitHubInstallIntent(client, start, options = {}) {
32239
+ const now = options.now ?? Date.now;
32240
+ const sleep2 = options.sleep ?? defaultSleep3;
32241
+ const signal = options.signal ?? new AbortController().signal;
32242
+ const path9 = `${START_PATH}/${encodeURIComponent(start.intentId)}`;
32243
+ while (true) {
32244
+ if (signal.aborted) throw signal.reason;
32245
+ const remainingBeforeSleep = start.expiresAt - now();
32246
+ if (remainingBeforeSleep <= 0) return expiredStatus(start);
32247
+ await sleep2(Math.min(start.pollAfterMs, remainingBeforeSleep), signal);
32248
+ if (signal.aborted) throw signal.reason;
32249
+ const remainingBeforeRequest = start.expiresAt - now();
32250
+ if (remainingBeforeRequest <= 0) return expiredStatus(start);
32251
+ let raw;
32252
+ try {
32253
+ raw = await client.get(path9, {
32254
+ signal: AbortSignal.any([
32255
+ signal,
32256
+ AbortSignal.timeout(Math.min(POLL_REQUEST_TIMEOUT_MS2, remainingBeforeRequest))
32257
+ ])
32258
+ });
32259
+ } catch (error) {
32260
+ if (signal.aborted) throw error;
32261
+ if (now() >= start.expiresAt) return expiredStatus(start);
32262
+ throw error;
32263
+ }
32264
+ const status = parseGitHubInstallIntentStatus(raw, start.expiresAt);
32265
+ if (!status) throw new Error("server returned an invalid GitHub install-intent status");
32266
+ if (status.status !== "pending" && status.status !== "claimed") return status;
32267
+ }
32268
+ }
32269
+
32270
+ // src/commands/auth.ts
32271
+ import { execFile } from "child_process";
32272
+ import { createHash as createHash6, randomBytes as randomBytes3 } from "crypto";
32273
+ import { existsSync as existsSync6, readFileSync as readFileSync7 } from "fs";
32274
+ import { createServer } from "http";
32275
+ import { platform } from "os";
32276
+
32168
32277
  // src/auth/api-keys.ts
32278
+ import { randomBytes as randomBytes2 } from "crypto";
32169
32279
  var USER_API_KEY_TIMEOUT_MS = 1e4;
32170
32280
  var USER_API_KEY_EXIT = {
32171
32281
  ok: 0,
@@ -33054,6 +33164,231 @@ async function exchangeCode(siteUrl, code, codeVerifier, redirectUri) {
33054
33164
  };
33055
33165
  }
33056
33166
 
33167
+ // src/commands/github.ts
33168
+ var BIND_TIMEOUT_MS = 1e4;
33169
+ var START_TIMEOUT_MS = 1e4;
33170
+ var EXIT_OK2 = 0;
33171
+ var EXIT_FAILURE = 1;
33172
+ var EXIT_UNBOUND = 2;
33173
+ var defaultDependencies2 = {
33174
+ cwd: () => process.cwd(),
33175
+ gitToplevel,
33176
+ bindRepositoryWithClient,
33177
+ getPinnedClient,
33178
+ createInstallIntent: createGitHubInstallIntent,
33179
+ pollInstallIntent: pollGitHubInstallIntent,
33180
+ openBrowser,
33181
+ now: Date.now
33182
+ };
33183
+ function errorMessage(error) {
33184
+ return error instanceof Error ? error.message : String(error);
33185
+ }
33186
+ function reportConnectFailure(error) {
33187
+ const message = errorMessage(error);
33188
+ const human = boundedHealthError(message) ?? "unknown error";
33189
+ process.stderr.write(`[prim] github connect failed: ${human}
33190
+ `);
33191
+ printJson({
33192
+ status: "error",
33193
+ error: message,
33194
+ ...error instanceof HttpError ? { httpStatus: error.status } : {}
33195
+ });
33196
+ process.exitCode = EXIT_FAILURE;
33197
+ }
33198
+ function reportBinding(binding, installAttempted = false) {
33199
+ if (binding.status === "connected") {
33200
+ process.stderr.write(
33201
+ `[prim] repository binding connected for GitHub origin ${binding.repositoryFullName}
33202
+ `
33203
+ );
33204
+ printJson(binding);
33205
+ process.exitCode = EXIT_OK2;
33206
+ return;
33207
+ }
33208
+ process.stderr.write(
33209
+ installAttempted ? `[prim] GitHub installation completed, but ${binding.repositoryFullName} was not granted admin access; repository binding remains unbound
33210
+ ` : `[prim] repository binding unbound for GitHub origin ${binding.repositoryFullName}; starting GitHub App installation
33211
+ `
33212
+ );
33213
+ printJson(binding);
33214
+ process.exitCode = EXIT_UNBOUND;
33215
+ }
33216
+ function terminalInstallError(status) {
33217
+ if (status.status === "failed_terminal") {
33218
+ return new Error(`GitHub installation failed: ${status.failureCode}`);
33219
+ }
33220
+ return new Error(`GitHub installation ${status.status}`);
33221
+ }
33222
+ async function completeInstallIntent(client, start, dependencies, browser) {
33223
+ process.stderr.write(`[prim] complete GitHub App installation at:
33224
+ ${start.browserUrl}
33225
+ `);
33226
+ if (browser) dependencies.openBrowser(start.browserUrl);
33227
+ return dependencies.pollInstallIntent(client, start, {
33228
+ now: dependencies.now
33229
+ });
33230
+ }
33231
+ async function runGithubConnect(dependencies = defaultDependencies2, options = {}) {
33232
+ const root = options.root ?? dependencies.gitToplevel(dependencies.cwd());
33233
+ if (!root) return { kind: "not-a-repo" };
33234
+ try {
33235
+ const client = await dependencies.getPinnedClient({
33236
+ signal: AbortSignal.timeout(BIND_TIMEOUT_MS)
33237
+ });
33238
+ const existing = await dependencies.bindRepositoryWithClient(root, client, {
33239
+ signal: AbortSignal.timeout(BIND_TIMEOUT_MS)
33240
+ });
33241
+ if (existing.status === "connected") {
33242
+ return { kind: "connected", binding: existing };
33243
+ }
33244
+ const start = await dependencies.createInstallIntent(client, {
33245
+ signal: AbortSignal.timeout(START_TIMEOUT_MS),
33246
+ now: dependencies.now
33247
+ });
33248
+ const status = await completeInstallIntent(
33249
+ client,
33250
+ start,
33251
+ dependencies,
33252
+ options.browser ?? true
33253
+ );
33254
+ if (status.status !== "consumed") throw terminalInstallError(status);
33255
+ process.stderr.write(
33256
+ `[prim] GitHub installation verified: ${status.adminRepositoryCount} admin repositories (${status.repositoryCount} total)
33257
+ `
33258
+ );
33259
+ const binding = await dependencies.bindRepositoryWithClient(root, client, {
33260
+ signal: AbortSignal.timeout(BIND_TIMEOUT_MS)
33261
+ });
33262
+ return binding.status === "connected" ? { kind: "connected", binding } : { kind: "unbound", binding, installAttempted: true };
33263
+ } catch (error) {
33264
+ return { kind: "error", error };
33265
+ }
33266
+ }
33267
+ async function performGithubConnect(dependencies = defaultDependencies2, options = {}) {
33268
+ const outcome = await runGithubConnect(dependencies, options);
33269
+ switch (outcome.kind) {
33270
+ case "not-a-repo":
33271
+ reportConnectFailure(
33272
+ new Error("not a git repository \u2014 run `prim github connect` inside a GitHub repository")
33273
+ );
33274
+ return;
33275
+ case "error":
33276
+ reportConnectFailure(outcome.error);
33277
+ return;
33278
+ case "connected":
33279
+ reportBinding(outcome.binding);
33280
+ return;
33281
+ case "unbound":
33282
+ reportBinding(outcome.binding, outcome.installAttempted);
33283
+ return;
33284
+ }
33285
+ }
33286
+ function registerGithubCommands(program3, dependencies = defaultDependencies2) {
33287
+ const github = program3.command("github").description("Manage GitHub repository bindings");
33288
+ github.command("connect").description("Install or reuse the Primitive GitHub App and bind the current GitHub origin").option("--no-browser", "Print the GitHub installation URL without opening a browser").action(async (options, command) => {
33289
+ const globals = command.optsWithGlobals();
33290
+ await performGithubConnect(dependencies, {
33291
+ browser: options.browser && globals.nonInteractive !== true
33292
+ });
33293
+ });
33294
+ }
33295
+
33296
+ // src/commands/activation.ts
33297
+ var CONNECT_PROMPT = "[prim] Connect this repository to Primitive via the GitHub App now?";
33298
+ async function maybeConnectRepository(root, globals) {
33299
+ if (isNonInteractive(globals)) return void 0;
33300
+ const approved = Boolean(globals.yes) || await askConfirmation(CONNECT_PROMPT, process.stderr);
33301
+ if (!approved) return void 0;
33302
+ const outcome = await runGithubConnect(void 0, { root, browser: true });
33303
+ if (outcome.kind === "connected") {
33304
+ process.stderr.write(
33305
+ `[prim] repository binding connected for GitHub origin ${outcome.binding.repositoryFullName}
33306
+ `
33307
+ );
33308
+ return outcome.binding;
33309
+ }
33310
+ if (outcome.kind === "error") {
33311
+ const detail = outcome.error instanceof Error ? outcome.error.message : String(outcome.error);
33312
+ process.stderr.write(`[prim] connect could not complete: ${detail}
33313
+ `);
33314
+ }
33315
+ return void 0;
33316
+ }
33317
+ async function applyActivation(active, globals = {}) {
33318
+ const root = gitToplevel();
33319
+ if (!root) {
33320
+ process.stderr.write(
33321
+ `[prim] not a git repository \u2014 run \`prim ${active ? "enable" : "disable"}\` inside a repo
33322
+ `
33323
+ );
33324
+ process.exit(1);
33325
+ }
33326
+ let phase = active ? "post-commit hook coverage" : "local deactivation";
33327
+ try {
33328
+ let binding;
33329
+ let postCommitHook;
33330
+ let postRewriteHook;
33331
+ if (active) {
33332
+ postCommitHook = ensureEffectivePostCommitHook(root).path;
33333
+ try {
33334
+ postRewriteHook = ensureEffectivePostRewriteHook(root).path;
33335
+ } catch (error) {
33336
+ const detail = error instanceof Error ? error.message : String(error);
33337
+ process.stderr.write(`[prim] post-rewrite hook coverage is degraded: ${detail}
33338
+ `);
33339
+ }
33340
+ phase = "repository binding";
33341
+ binding = await bindRepository(root);
33342
+ phase = "local activation";
33343
+ }
33344
+ setRepoActive(root, active);
33345
+ if (active && binding?.status === "unbound") {
33346
+ const connected = await maybeConnectRepository(root, globals);
33347
+ if (connected) binding = connected;
33348
+ }
33349
+ await daemonRequest("statusline_invalidate", {}, { timeoutMs: 250 });
33350
+ if (binding?.status === "unbound") {
33351
+ process.stderr.write(`[prim] Prim is enabled locally in ${root}
33352
+ `);
33353
+ process.stderr.write(
33354
+ "[prim] repository is not connected to Primitive; Moves still ingest into the team graph without repository-specific file attribution, Conflict Gate verification, or commit correlation\n"
33355
+ );
33356
+ process.stderr.write(
33357
+ `[prim] ask an organization owner or administrator to grant Primitive's GitHub App access to this repository through Primitive's GitHub App onboarding; binding retries automatically at the next agent SessionStart
33358
+ `
33359
+ );
33360
+ } else {
33361
+ process.stderr.write(`[prim] prim ${active ? "enabled" : "disabled"} in ${root}
33362
+ `);
33363
+ }
33364
+ printJson({
33365
+ active,
33366
+ repo: root,
33367
+ ...binding ? {
33368
+ bindingStatus: binding.status,
33369
+ repositoryFullName: binding.repositoryFullName,
33370
+ ...binding.status === "connected" ? { repoSyncId: binding.repoSyncId } : {}
33371
+ } : {},
33372
+ ...postCommitHook ? { postCommitHook } : {},
33373
+ ...postRewriteHook ? { postRewriteHook } : {}
33374
+ });
33375
+ } catch (err) {
33376
+ const detail = err instanceof Error ? err.message : String(err);
33377
+ process.stderr.write(
33378
+ active ? `[prim] failed to enable prim during ${phase}: ${detail}
33379
+ ` : `[prim] failed to disable prim: ${detail}
33380
+ `
33381
+ );
33382
+ process.exit(1);
33383
+ }
33384
+ }
33385
+ function registerActivationCommands(program3) {
33386
+ program3.command("enable").description("Activate prim's hooks in this repo (git config prim.active=true)").action((_opts, command) => applyActivation(true, command.optsWithGlobals()));
33387
+ program3.command("disable").description("Mute prim's hooks in this repo (git config prim.active=false)").action(
33388
+ (_opts, command) => applyActivation(false, command.optsWithGlobals())
33389
+ );
33390
+ }
33391
+
33057
33392
  // src/commands/claude-install.ts
33058
33393
  import { existsSync as existsSync8, readFileSync as readFileSync9 } from "fs";
33059
33394
  import { homedir as homedir4 } from "os";
@@ -34855,7 +35190,7 @@ function atomicWrite3(path9, content, mode, validate3) {
34855
35190
  }
34856
35191
  return true;
34857
35192
  }
34858
- function defaultSleep3(ms) {
35193
+ function defaultSleep4(ms) {
34859
35194
  return new Promise((resolvePromise) => setTimeout(resolvePromise, ms));
34860
35195
  }
34861
35196
  function validatePlist(path9, timeoutMs2) {
@@ -35174,7 +35509,7 @@ async function ensureMacDaemonLocked(options) {
35174
35509
  PLIST_FILE_MODE,
35175
35510
  options.validatePlist ?? validatePlist
35176
35511
  );
35177
- const sleep2 = options.sleep ?? defaultSleep3;
35512
+ const sleep2 = options.sleep ?? defaultSleep4;
35178
35513
  const nowMs = options.nowMs ?? performance.now.bind(performance);
35179
35514
  const deadlineMs = nowMs() + TRANSITION_TIMEOUT_MS;
35180
35515
  const inspect = options.inspectDaemon ?? inspectDaemon;
@@ -35228,7 +35563,7 @@ async function bootoutMacDaemon(options = {}) {
35228
35563
  async function bootoutMacDaemonLocked(options) {
35229
35564
  const runner = options.runner ?? runLaunchctl;
35230
35565
  const inspect = options.inspectDaemon ?? inspectDaemon;
35231
- const sleep2 = options.sleep ?? defaultSleep3;
35566
+ const sleep2 = options.sleep ?? defaultSleep4;
35232
35567
  const nowMs = options.nowMs ?? performance.now.bind(performance);
35233
35568
  const deadlineMs = nowMs() + TRANSITION_TIMEOUT_MS;
35234
35569
  const paths = launchdPaths(options);
@@ -35282,73 +35617,6 @@ async function bootoutMacDaemonLocked(options) {
35282
35617
  return { wasLoaded, legacyStopped, service };
35283
35618
  }
35284
35619
 
35285
- // src/lib/ansi.ts
35286
- var ANSI_CODES = {
35287
- yellow: "\x1B[33m",
35288
- magenta: "\x1B[35m",
35289
- blue: "\x1B[34m",
35290
- cyan: "\x1B[36m",
35291
- green: "\x1B[32m",
35292
- red: "\x1B[31m",
35293
- // 256-color approximation of the orange trigger marker (xterm 208).
35294
- orange: "\x1B[38;5;208m",
35295
- gray: "\x1B[90m"
35296
- };
35297
- var ANSI_RESET = "\x1B[0m";
35298
- var ANSI_DIM = "\x1B[2m";
35299
- var ANSI_BOLD = "\x1B[1m";
35300
- function supportsColor2() {
35301
- if (process.env.NO_COLOR !== void 0 && process.env.NO_COLOR !== "") {
35302
- return false;
35303
- }
35304
- return process.stderr.isTTY === true;
35305
- }
35306
- function color(text, c) {
35307
- if (!supportsColor2()) {
35308
- return text;
35309
- }
35310
- return `${ANSI_CODES[c]}${text}${ANSI_RESET}`;
35311
- }
35312
- function dim(text) {
35313
- if (!supportsColor2()) {
35314
- return text;
35315
- }
35316
- return `${ANSI_DIM}${text}${ANSI_RESET}`;
35317
- }
35318
- function bold(text) {
35319
- if (!supportsColor2()) {
35320
- return text;
35321
- }
35322
- return `${ANSI_BOLD}${text}${ANSI_RESET}`;
35323
- }
35324
- var AREA_COLORS = {
35325
- auth: "yellow",
35326
- data: "magenta",
35327
- mobile: "blue",
35328
- infra: "cyan",
35329
- ui: "green",
35330
- billing: "orange",
35331
- api: "blue",
35332
- docs: "gray",
35333
- testing: "gray"
35334
- };
35335
- function colorForArea(area) {
35336
- if (!area) {
35337
- return "gray";
35338
- }
35339
- return AREA_COLORS[area] ?? "gray";
35340
- }
35341
- var MAX_HEALTH_ERROR_LENGTH = 240;
35342
- function boundedHealthError(value) {
35343
- if (!value) return void 0;
35344
- const clean = terminalSafeLine(value);
35345
- if (!clean) return void 0;
35346
- return clean.length <= MAX_HEALTH_ERROR_LENGTH ? clean : `${clean.slice(0, MAX_HEALTH_ERROR_LENGTH - 1)}\u2026`;
35347
- }
35348
- function stripAnsi2(text) {
35349
- return text.replace(/\u001b\[[0-9;]*m/g, "");
35350
- }
35351
-
35352
35620
  // src/lib/presence.ts
35353
35621
  var DECISION_ORIGIN = "https://app.getprimitive.ai";
35354
35622
  function validDecisionUrl(value) {
@@ -35468,7 +35736,7 @@ var READY_POLL_MS2 = 100;
35468
35736
  var READY_PROBE_TIMEOUT_MS2 = 250;
35469
35737
  var HEALTHY_TIMEOUT_MS = 3e4;
35470
35738
  var HEALTHY_POLL_MS = 250;
35471
- var EXIT_OK2 = 0;
35739
+ var EXIT_OK3 = 0;
35472
35740
  var EXIT_NOT_RUNNING = 2;
35473
35741
  var EXIT_BOOTING = 3;
35474
35742
  function formatDaemonLifecycleMessage(message, decisionIngestion) {
@@ -35849,7 +36117,7 @@ function classifyStatus(pidAlive, responding, snapshot, pid) {
35849
36117
  };
35850
36118
  }
35851
36119
  if (!snapshot) {
35852
- return { json: { running: true, responding: true }, exitCode: EXIT_OK2 };
36120
+ return { json: { running: true, responding: true }, exitCode: EXIT_OK3 };
35853
36121
  }
35854
36122
  if (snapshot.healthy === false) {
35855
36123
  return {
@@ -35857,7 +36125,7 @@ function classifyStatus(pidAlive, responding, snapshot, pid) {
35857
36125
  exitCode: EXIT_BOOTING
35858
36126
  };
35859
36127
  }
35860
- return { json: { running: true, responding: true, ...snapshot }, exitCode: EXIT_OK2 };
36128
+ return { json: { running: true, responding: true, ...snapshot }, exitCode: EXIT_OK3 };
35861
36129
  }
35862
36130
  function classifyLaunchdStatus(service, responding, snapshot, disabled) {
35863
36131
  if (!service) {
@@ -35955,7 +36223,7 @@ function classifyLaunchdStatus(service, responding, snapshot, disabled) {
35955
36223
  disabled,
35956
36224
  ...snapshot
35957
36225
  },
35958
- exitCode: EXIT_OK2
36226
+ exitCode: EXIT_OK3
35959
36227
  };
35960
36228
  }
35961
36229
  function formatDaemonSnapshotMessage(snapshot, supervised, decisionIngestion, bindingState) {
@@ -36005,7 +36273,7 @@ async function detachedDaemonStatus() {
36005
36273
  writeLiveSnapshot(snapshot);
36006
36274
  }
36007
36275
  console.log(JSON.stringify(json, null, 2));
36008
- if (exitCode !== EXIT_OK2 && !process.exitCode) {
36276
+ if (exitCode !== EXIT_OK3 && !process.exitCode) {
36009
36277
  process.exitCode = exitCode;
36010
36278
  }
36011
36279
  }
@@ -36055,7 +36323,7 @@ async function macDaemonStatus() {
36055
36323
  writeLiveSnapshot(snapshot, true);
36056
36324
  }
36057
36325
  console.log(JSON.stringify(json, null, 2));
36058
- if (exitCode !== EXIT_OK2 && !process.exitCode) process.exitCode = exitCode;
36326
+ if (exitCode !== EXIT_OK3 && !process.exitCode) process.exitCode = exitCode;
36059
36327
  }
36060
36328
  async function daemonStatus() {
36061
36329
  if (process.platform === "darwin") {
@@ -36672,7 +36940,7 @@ var DECISION_LIFECYCLE_EXIT = {
36672
36940
  server: 3,
36673
36941
  notFound: 4
36674
36942
  };
36675
- var defaultDependencies2 = {
36943
+ var defaultDependencies3 = {
36676
36944
  getClient,
36677
36945
  signal: () => AbortSignal.timeout(DECISION_LIFECYCLE_TIMEOUT_MS),
36678
36946
  writeStdout: (value) => console.log(value),
@@ -36885,19 +37153,19 @@ async function executeLifecycle(operation, request2, dependencies) {
36885
37153
  return writeFailure2(operation, classifyError2(error), dependencies);
36886
37154
  }
36887
37155
  }
36888
- function publishDecision(id, dependencies = defaultDependencies2) {
37156
+ function publishDecision(id, dependencies = defaultDependencies3) {
36889
37157
  const request2 = { id };
36890
37158
  return executeLifecycle("publish", request2, dependencies);
36891
37159
  }
36892
- function restoreDecision(id, dependencies = defaultDependencies2) {
37160
+ function restoreDecision(id, dependencies = defaultDependencies3) {
36893
37161
  const request2 = { id };
36894
37162
  return executeLifecycle("restore", request2, dependencies);
36895
37163
  }
36896
- function ratifyDecision(id, dependencies = defaultDependencies2) {
37164
+ function ratifyDecision(id, dependencies = defaultDependencies3) {
36897
37165
  const request2 = { id };
36898
37166
  return executeLifecycle("ratify", request2, dependencies);
36899
37167
  }
36900
- function supersedeDecision(id, by, dependencies = defaultDependencies2) {
37168
+ function supersedeDecision(id, by, dependencies = defaultDependencies3) {
36901
37169
  const request2 = { id, by };
36902
37170
  return executeLifecycle("supersede", request2, dependencies);
36903
37171
  }
@@ -37736,26 +38004,9 @@ function formatDecisionsWarning(result) {
37736
38004
  return lines.join("\n");
37737
38005
  }
37738
38006
 
37739
- // src/lib/confirmation.ts
37740
- function isNonInteractive(globals) {
37741
- return Boolean(globals.nonInteractive || process.env.CI || process.env.PRIM_NON_INTERACTIVE);
37742
- }
37743
- async function askConfirmation(question, output = process.stdout) {
37744
- if (!process.stdin.isTTY) return false;
37745
- const { createInterface } = await import("readline/promises");
37746
- const rl = createInterface({ input: process.stdin, output });
37747
- try {
37748
- const answer = await rl.question(`${question} [y/N] `);
37749
- const normalized = answer.trim().toLowerCase();
37750
- return normalized === "y" || normalized === "yes";
37751
- } finally {
37752
- rl.close();
37753
- }
37754
- }
37755
-
37756
38007
  // src/commands/decisions.ts
37757
38008
  var EXIT_NOT_FOUND = 4;
37758
- var EXIT_FAILURE = 1;
38009
+ var EXIT_FAILURE2 = 1;
37759
38010
  var EXIT_USAGE = 2;
37760
38011
  function handleRepairCommandError(error) {
37761
38012
  if (error instanceof RepairProposalNotFoundError) {
@@ -37772,12 +38023,12 @@ function handleRepairCommandError(error) {
37772
38023
  console.error(
37773
38024
  `[prim] ${error instanceof RepairAuthorizationError ? error.message : "Not authorized to review commit repairs; active organization membership and current repository authorization are required"}`
37774
38025
  );
37775
- process.exitCode = EXIT_FAILURE;
38026
+ process.exitCode = EXIT_FAILURE2;
37776
38027
  return true;
37777
38028
  }
37778
38029
  if (error instanceof RepairEndpointVersionError || error instanceof RepairListContractError) {
37779
38030
  console.error(`[prim] ${error.message}`);
37780
- process.exitCode = EXIT_FAILURE;
38031
+ process.exitCode = EXIT_FAILURE2;
37781
38032
  return true;
37782
38033
  }
37783
38034
  return false;
@@ -39802,220 +40053,6 @@ function registerDoctorCommands(program3) {
39802
40053
  });
39803
40054
  }
39804
40055
 
39805
- // src/lib/github-install-intent.ts
39806
- var PROTOCOL_VERSION = 1;
39807
- var MODE = "install_intent_v1";
39808
- var START_PATH = "/api/cli/github/install-intents";
39809
- var POLL_REQUEST_TIMEOUT_MS2 = 1e4;
39810
- var MAX_INTENT_LIFETIME_MS = 15 * 6e4;
39811
- var REPOSITORY_IDENTITY_MIGRATION_REQUIRED = "repository_identity_migration_required";
39812
- function isRepositoryIdentityMigrationRequiredStatus(value) {
39813
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
39814
- return false;
39815
- }
39816
- const record2 = value;
39817
- if (record2.failureCode !== REPOSITORY_IDENTITY_MIGRATION_REQUIRED) {
39818
- return false;
39819
- }
39820
- return isGitHubInstallIntentStatusResponse({
39821
- ...record2,
39822
- failureCode: "proof_commit_failed"
39823
- });
39824
- }
39825
- function parseGitHubInstallIntentStart(value, now = Date.now()) {
39826
- if (!isGitHubInstallIntentStartResponse(value) || value.expiresAt <= now || value.expiresAt - now > MAX_INTENT_LIFETIME_MS) {
39827
- return null;
39828
- }
39829
- return value;
39830
- }
39831
- function parseGitHubInstallIntentStatus(value, expectedExpiresAt) {
39832
- if (isGitHubInstallIntentStatusResponse(value)) {
39833
- return value.expiresAt === expectedExpiresAt ? value : null;
39834
- }
39835
- if (!isRepositoryIdentityMigrationRequiredStatus(value) || value.expiresAt !== expectedExpiresAt) {
39836
- return null;
39837
- }
39838
- return value;
39839
- }
39840
- async function createGitHubInstallIntent(client, options = {}) {
39841
- const { now = Date.now, ...requestOptions } = options;
39842
- const raw = await client.post(START_PATH, void 0, requestOptions);
39843
- const parsed = parseGitHubInstallIntentStart(raw, now());
39844
- if (!parsed) throw new Error("server returned an invalid GitHub install-intent response");
39845
- return parsed;
39846
- }
39847
- function defaultSleep4(milliseconds, signal) {
39848
- if (signal.aborted) return Promise.reject(signal.reason);
39849
- return new Promise((resolvePromise, reject) => {
39850
- const onAbort = () => {
39851
- clearTimeout(timer);
39852
- reject(signal.reason);
39853
- };
39854
- const timer = setTimeout(() => {
39855
- signal.removeEventListener("abort", onAbort);
39856
- resolvePromise();
39857
- }, milliseconds);
39858
- signal.addEventListener("abort", onAbort, { once: true });
39859
- });
39860
- }
39861
- function expiredStatus(start) {
39862
- return {
39863
- protocolVersion: PROTOCOL_VERSION,
39864
- mode: MODE,
39865
- found: true,
39866
- status: "expired",
39867
- expiresAt: start.expiresAt,
39868
- closedAt: start.expiresAt
39869
- };
39870
- }
39871
- async function pollGitHubInstallIntent(client, start, options = {}) {
39872
- const now = options.now ?? Date.now;
39873
- const sleep2 = options.sleep ?? defaultSleep4;
39874
- const signal = options.signal ?? new AbortController().signal;
39875
- const path9 = `${START_PATH}/${encodeURIComponent(start.intentId)}`;
39876
- while (true) {
39877
- if (signal.aborted) throw signal.reason;
39878
- const remainingBeforeSleep = start.expiresAt - now();
39879
- if (remainingBeforeSleep <= 0) return expiredStatus(start);
39880
- await sleep2(Math.min(start.pollAfterMs, remainingBeforeSleep), signal);
39881
- if (signal.aborted) throw signal.reason;
39882
- const remainingBeforeRequest = start.expiresAt - now();
39883
- if (remainingBeforeRequest <= 0) return expiredStatus(start);
39884
- let raw;
39885
- try {
39886
- raw = await client.get(path9, {
39887
- signal: AbortSignal.any([
39888
- signal,
39889
- AbortSignal.timeout(Math.min(POLL_REQUEST_TIMEOUT_MS2, remainingBeforeRequest))
39890
- ])
39891
- });
39892
- } catch (error) {
39893
- if (signal.aborted) throw error;
39894
- if (now() >= start.expiresAt) return expiredStatus(start);
39895
- throw error;
39896
- }
39897
- const status = parseGitHubInstallIntentStatus(raw, start.expiresAt);
39898
- if (!status) throw new Error("server returned an invalid GitHub install-intent status");
39899
- if (status.status !== "pending" && status.status !== "claimed") return status;
39900
- }
39901
- }
39902
-
39903
- // src/commands/github.ts
39904
- var BIND_TIMEOUT_MS = 1e4;
39905
- var START_TIMEOUT_MS = 1e4;
39906
- var EXIT_OK3 = 0;
39907
- var EXIT_FAILURE2 = 1;
39908
- var EXIT_UNBOUND = 2;
39909
- var defaultDependencies3 = {
39910
- cwd: () => process.cwd(),
39911
- gitToplevel,
39912
- bindRepositoryWithClient,
39913
- getPinnedClient,
39914
- createInstallIntent: createGitHubInstallIntent,
39915
- pollInstallIntent: pollGitHubInstallIntent,
39916
- openBrowser,
39917
- now: Date.now
39918
- };
39919
- function errorMessage(error) {
39920
- return error instanceof Error ? error.message : String(error);
39921
- }
39922
- function reportConnectFailure(error) {
39923
- const message = errorMessage(error);
39924
- const human = boundedHealthError(message) ?? "unknown error";
39925
- process.stderr.write(`[prim] github connect failed: ${human}
39926
- `);
39927
- printJson({
39928
- status: "error",
39929
- error: message,
39930
- ...error instanceof HttpError ? { httpStatus: error.status } : {}
39931
- });
39932
- process.exitCode = EXIT_FAILURE2;
39933
- }
39934
- function reportBinding(binding, installAttempted = false) {
39935
- if (binding.status === "connected") {
39936
- process.stderr.write(
39937
- `[prim] repository binding connected for GitHub origin ${binding.repositoryFullName}
39938
- `
39939
- );
39940
- printJson(binding);
39941
- process.exitCode = EXIT_OK3;
39942
- return;
39943
- }
39944
- process.stderr.write(
39945
- installAttempted ? `[prim] GitHub installation completed, but ${binding.repositoryFullName} was not granted admin access; repository binding remains unbound
39946
- ` : `[prim] repository binding unbound for GitHub origin ${binding.repositoryFullName}; starting GitHub App installation
39947
- `
39948
- );
39949
- printJson(binding);
39950
- process.exitCode = EXIT_UNBOUND;
39951
- }
39952
- function terminalInstallError(status) {
39953
- if (status.status === "failed_terminal") {
39954
- return new Error(`GitHub installation failed: ${status.failureCode}`);
39955
- }
39956
- return new Error(`GitHub installation ${status.status}`);
39957
- }
39958
- async function completeInstallIntent(client, start, dependencies, browser) {
39959
- process.stderr.write(`[prim] complete GitHub App installation at:
39960
- ${start.browserUrl}
39961
- `);
39962
- if (browser) dependencies.openBrowser(start.browserUrl);
39963
- return dependencies.pollInstallIntent(client, start, {
39964
- now: dependencies.now
39965
- });
39966
- }
39967
- async function performGithubConnect(dependencies = defaultDependencies3, options = {}) {
39968
- const root = dependencies.gitToplevel(dependencies.cwd());
39969
- if (!root) {
39970
- reportConnectFailure(
39971
- new Error("not a git repository \u2014 run `prim github connect` inside a GitHub repository")
39972
- );
39973
- return;
39974
- }
39975
- try {
39976
- const client = await dependencies.getPinnedClient({
39977
- signal: AbortSignal.timeout(BIND_TIMEOUT_MS)
39978
- });
39979
- const existing = await dependencies.bindRepositoryWithClient(root, client, {
39980
- signal: AbortSignal.timeout(BIND_TIMEOUT_MS)
39981
- });
39982
- if (existing.status === "connected") {
39983
- reportBinding(existing);
39984
- return;
39985
- }
39986
- const start = await dependencies.createInstallIntent(client, {
39987
- signal: AbortSignal.timeout(START_TIMEOUT_MS),
39988
- now: dependencies.now
39989
- });
39990
- const status = await completeInstallIntent(
39991
- client,
39992
- start,
39993
- dependencies,
39994
- options.browser ?? true
39995
- );
39996
- if (status.status !== "consumed") throw terminalInstallError(status);
39997
- process.stderr.write(
39998
- `[prim] GitHub installation verified: ${status.adminRepositoryCount} admin repositories (${status.repositoryCount} total)
39999
- `
40000
- );
40001
- const binding = await dependencies.bindRepositoryWithClient(root, client, {
40002
- signal: AbortSignal.timeout(BIND_TIMEOUT_MS)
40003
- });
40004
- reportBinding(binding, true);
40005
- } catch (error) {
40006
- reportConnectFailure(error);
40007
- }
40008
- }
40009
- function registerGithubCommands(program3, dependencies = defaultDependencies3) {
40010
- const github = program3.command("github").description("Manage GitHub repository bindings");
40011
- github.command("connect").description("Install or reuse the Primitive GitHub App and bind the current GitHub origin").option("--no-browser", "Print the GitHub installation URL without opening a browser").action(async (options, command) => {
40012
- const globals = command.optsWithGlobals();
40013
- await performGithubConnect(dependencies, {
40014
- browser: options.browser && globals.nonInteractive !== true
40015
- });
40016
- });
40017
- }
40018
-
40019
40056
  // src/commands/hooks.ts
40020
40057
  import { execFileSync as execFileSync5 } from "child_process";
40021
40058
  import {
@@ -40333,14 +40370,9 @@ function writeOwnHooks() {
40333
40370
  mkdirSync11(PRIM_GIT_HOOKS_DIR, { recursive: true });
40334
40371
  }
40335
40372
  for (const spec of HOOKS) {
40336
- const path9 = resolve7(PRIM_GIT_HOOKS_DIR, spec.hookName);
40337
- if (spec.hookName === POST_COMMIT.hookName) {
40338
- ensurePostCommitHookAtPath(path9, globalHookScript(spec));
40339
- } else if (spec.hookName === POST_REWRITE.hookName) {
40340
- writeFileSync8(path9, globalHookScript(spec), { mode: 493 });
40341
- } else {
40342
- writeFileSync8(path9, globalHookScript(spec), { mode: 493 });
40343
- }
40373
+ writeFileSync8(resolve7(PRIM_GIT_HOOKS_DIR, spec.hookName), globalHookScript(spec), {
40374
+ mode: 493
40375
+ });
40344
40376
  }
40345
40377
  for (const name of PASSTHROUGH_HOOKS) {
40346
40378
  writeFileSync8(resolve7(PRIM_GIT_HOOKS_DIR, name), passThroughScript(name), { mode: 493 });
@@ -41408,6 +41440,7 @@ function registerSetupCommand(program3, dependencies = {}) {
41408
41440
  const agent = agentInput;
41409
41441
  const scope = opts.scope;
41410
41442
  const self = process.argv[1];
41443
+ const globals = program3.optsWithGlobals();
41411
41444
  const run = dependencies.run ?? ((args, capture = false) => {
41412
41445
  const r = spawnSync2(process.execPath, [self, ...args], {
41413
41446
  stdio: capture ? ["inherit", "pipe", "ignore"] : "inherit",
@@ -41477,7 +41510,12 @@ function registerSetupCommand(program3, dependencies = {}) {
41477
41510
  const enableStep = setupSteps.find((candidate) => candidate.key === "enable");
41478
41511
  if (enableStep) {
41479
41512
  note(`${enableStep.label} \xB7 installing\u2026`);
41480
- const { code } = run(enableStep.args);
41513
+ const enableArgs = [
41514
+ ...enableStep.args,
41515
+ ...globals.yes ? ["--yes"] : [],
41516
+ ...globals.nonInteractive ? ["--non-interactive"] : []
41517
+ ];
41518
+ const { code } = run(enableArgs);
41481
41519
  results[enableStep.key] = code === 0 ? "ok" : enableStep.required ? "failed" : "skipped";
41482
41520
  }
41483
41521
  const healthStep = setupSteps.find((candidate) => candidate.key === "health");