@openthink/stamp 2.0.1 → 2.0.2

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.
package/dist/index.js CHANGED
@@ -59,8 +59,8 @@ import { Command } from "commander";
59
59
 
60
60
  // src/commands/bootstrap.ts
61
61
  import { execFileSync as execFileSync4 } from "child_process";
62
- import { existsSync as existsSync7, readFileSync as readFileSync6, readdirSync, statSync, writeFileSync as writeFileSync5 } from "fs";
63
- import { dirname as dirname3, join as join3 } from "path";
62
+ import { existsSync as existsSync7, readFileSync as readFileSync7, readdirSync, statSync, writeFileSync as writeFileSync5 } from "fs";
63
+ import { dirname as dirname4, join as join4 } from "path";
64
64
 
65
65
  // src/lib/agentsMd.ts
66
66
  import { existsSync, readFileSync, writeFileSync } from "fs";
@@ -3166,9 +3166,38 @@ function readLineSync() {
3166
3166
  return out;
3167
3167
  }
3168
3168
 
3169
+ // src/lib/version.ts
3170
+ import { readFileSync as readFileSync4 } from "fs";
3171
+ import { dirname, join as join3 } from "path";
3172
+ import { fileURLToPath as fileURLToPath2 } from "url";
3173
+ function readPackageVersion() {
3174
+ const here = dirname(fileURLToPath2(import.meta.url));
3175
+ for (let dir = here, i = 0; i < 6; i++) {
3176
+ try {
3177
+ const raw = readFileSync4(join3(dir, "package.json"), "utf8");
3178
+ const pkg = JSON.parse(raw);
3179
+ if (pkg.name === "@openthink/stamp" && pkg.version) return pkg.version;
3180
+ } catch {
3181
+ }
3182
+ const parent = dirname(dir);
3183
+ if (parent === dir) break;
3184
+ dir = parent;
3185
+ }
3186
+ throw new Error("could not locate @openthink/stamp package.json to read version");
3187
+ }
3188
+
3169
3189
  // src/lib/deprecationNotice.ts
3170
- function maybePrintDeprecationNotice() {
3190
+ function maybePrintDeprecationNotice(versionOverride) {
3171
3191
  if (process.env.STAMP_SUPPRESS_DEPRECATION === "1") return;
3192
+ let major = null;
3193
+ try {
3194
+ const version = versionOverride ?? readPackageVersion();
3195
+ const m = /^(\d+)\./.exec(version);
3196
+ if (m) major = Number.parseInt(m[1], 10);
3197
+ } catch {
3198
+ return;
3199
+ }
3200
+ if (major === null || major >= 2) return;
3172
3201
  process.stderr.write(
3173
3202
  "warning: stamp 1.x is in maintenance \u2014 the server-attested 2.x line is active. See docs/migration-1.x-to-2.x.md. Suppress: STAMP_SUPPRESS_DEPRECATION=1.\n"
3174
3203
  );
@@ -3763,12 +3792,12 @@ ${close}`;
3763
3792
  import {
3764
3793
  existsSync as existsSync3,
3765
3794
  mkdirSync,
3766
- readFileSync as readFileSync4,
3795
+ readFileSync as readFileSync5,
3767
3796
  renameSync,
3768
3797
  unlinkSync,
3769
3798
  writeFileSync as writeFileSync2
3770
3799
  } from "fs";
3771
- import { dirname } from "path";
3800
+ import { dirname as dirname2 } from "path";
3772
3801
  import { parse as parseYaml6, stringify as stringifyYaml } from "yaml";
3773
3802
  var DEFAULT_REVIEWER_MODELS = {
3774
3803
  security: "claude-sonnet-4-6",
@@ -3788,7 +3817,7 @@ function loadUserConfig() {
3788
3817
  if (!existsSync3(path2)) return null;
3789
3818
  let raw;
3790
3819
  try {
3791
- raw = readFileSync4(path2, "utf8");
3820
+ raw = readFileSync5(path2, "utf8");
3792
3821
  } catch (err) {
3793
3822
  throw new Error(
3794
3823
  `failed to read ${path2}: ${err instanceof Error ? err.message : String(err)}`
@@ -3848,7 +3877,7 @@ function stringifyUserConfig(cfg) {
3848
3877
  }
3849
3878
  function writeUserConfig(cfg) {
3850
3879
  const path2 = userConfigPath();
3851
- const dir = dirname(path2);
3880
+ const dir = dirname2(path2);
3852
3881
  if (!existsSync3(dir)) mkdirSync(dir, { recursive: true, mode: 448 });
3853
3882
  const tmp = `${path2}.tmp.${process.pid}`;
3854
3883
  writeFileSync2(tmp, stringifyUserConfig(cfg), { mode: 384 });
@@ -4659,7 +4688,7 @@ function stripLastLineVerdict(text) {
4659
4688
 
4660
4689
  // src/lib/llmNotice.ts
4661
4690
  import { existsSync as existsSync4, mkdirSync as mkdirSync3, writeFileSync as writeFileSync4 } from "fs";
4662
- import { dirname as dirname2 } from "path";
4691
+ import { dirname as dirname3 } from "path";
4663
4692
  function maybePrintLlmNotice(repoRoot) {
4664
4693
  if (process.env.STAMP_SUPPRESS_LLM_NOTICE === "1") return;
4665
4694
  const marker = stampLlmNoticeMarkerPath(repoRoot);
@@ -4672,7 +4701,7 @@ function maybePrintLlmNotice(repoRoot) {
4672
4701
  `
4673
4702
  );
4674
4703
  try {
4675
- mkdirSync3(dirname2(marker), { recursive: true });
4704
+ mkdirSync3(dirname3(marker), { recursive: true });
4676
4705
  writeFileSync4(marker, `${(/* @__PURE__ */ new Date()).toISOString()}
4677
4706
  `);
4678
4707
  } catch {
@@ -4912,7 +4941,7 @@ import { spawnSync as spawnSync4 } from "child_process";
4912
4941
  import { createInterface } from "readline";
4913
4942
 
4914
4943
  // src/lib/serverConfig.ts
4915
- import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
4944
+ import { existsSync as existsSync5, readFileSync as readFileSync6 } from "fs";
4916
4945
  import { parse as parseYaml7 } from "yaml";
4917
4946
  var DEFAULT_USER = "git";
4918
4947
  var DEFAULT_REPO_ROOT = "/srv/git";
@@ -4942,7 +4971,7 @@ function loadServerConfig() {
4942
4971
  if (!existsSync5(path2)) return null;
4943
4972
  let raw;
4944
4973
  try {
4945
- raw = readFileSync5(path2, "utf8");
4974
+ raw = readFileSync6(path2, "utf8");
4946
4975
  } catch (err) {
4947
4976
  throw new Error(
4948
4977
  `failed to read ${path2}: ${err instanceof Error ? err.message : String(err)}`
@@ -5921,27 +5950,27 @@ function readSeedDir(seedDir) {
5921
5950
  if (!existsSync7(seedDir) || !statSync(seedDir).isDirectory()) {
5922
5951
  throw new Error(`--from path is not a directory: ${seedDir}`);
5923
5952
  }
5924
- const configPath = join3(seedDir, "config.yml");
5953
+ const configPath = join4(seedDir, "config.yml");
5925
5954
  if (!existsSync7(configPath)) {
5926
5955
  throw new Error(`--from dir missing config.yml: ${configPath}`);
5927
5956
  }
5928
- const reviewersDir = join3(seedDir, "reviewers");
5957
+ const reviewersDir = join4(seedDir, "reviewers");
5929
5958
  if (!existsSync7(reviewersDir) || !statSync(reviewersDir).isDirectory()) {
5930
5959
  throw new Error(`--from dir missing reviewers/ subdirectory: ${reviewersDir}`);
5931
5960
  }
5932
- const yaml = readFileSync6(configPath, "utf8");
5961
+ const yaml = readFileSync7(configPath, "utf8");
5933
5962
  const config2 = parseConfigFromYaml(yaml);
5934
5963
  const reviewerFiles = /* @__PURE__ */ new Map();
5935
5964
  for (const entry of readdirSync(reviewersDir)) {
5936
- const full = join3(reviewersDir, entry);
5965
+ const full = join4(reviewersDir, entry);
5937
5966
  if (statSync(full).isFile()) {
5938
- reviewerFiles.set(`.stamp/reviewers/${entry}`, readFileSync6(full, "utf8"));
5967
+ reviewerFiles.set(`.stamp/reviewers/${entry}`, readFileSync7(full, "utf8"));
5939
5968
  }
5940
5969
  }
5941
5970
  let mirrorYml;
5942
- const mirrorPath = join3(seedDir, "mirror.yml");
5971
+ const mirrorPath = join4(seedDir, "mirror.yml");
5943
5972
  if (existsSync7(mirrorPath)) {
5944
- mirrorYml = readFileSync6(mirrorPath, "utf8");
5973
+ mirrorYml = readFileSync7(mirrorPath, "utf8");
5945
5974
  }
5946
5975
  return { config: config2, reviewerFiles, mirrorYml };
5947
5976
  }
@@ -5949,12 +5978,12 @@ function writeBootstrapFiles(repoRoot, plan) {
5949
5978
  ensureDir(stampConfigDir(repoRoot));
5950
5979
  ensureDir(stampReviewersDir(repoRoot));
5951
5980
  for (const { path: path2, content } of plan.reviewerFiles.values()) {
5952
- const full = join3(repoRoot, path2);
5953
- ensureDir(dirname3(full));
5981
+ const full = join4(repoRoot, path2);
5982
+ ensureDir(dirname4(full));
5954
5983
  writeFileSync5(full, content);
5955
5984
  }
5956
5985
  if (plan.mirrorYml !== void 0) {
5957
- writeFileSync5(join3(repoRoot, ".stamp/mirror.yml"), plan.mirrorYml);
5986
+ writeFileSync5(join4(repoRoot, ".stamp/mirror.yml"), plan.mirrorYml);
5958
5987
  }
5959
5988
  writeFileSync5(stampConfigFile(repoRoot), stringifyConfig(plan.newConfig));
5960
5989
  }
@@ -5997,8 +6026,8 @@ function branchExists(name, cwd) {
5997
6026
  }
5998
6027
 
5999
6028
  // src/commands/init.ts
6000
- import { existsSync as existsSync9, readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "fs";
6001
- import { dirname as dirname4, join as join5 } from "path";
6029
+ import { existsSync as existsSync9, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "fs";
6030
+ import { dirname as dirname5, join as join6 } from "path";
6002
6031
 
6003
6032
  // src/lib/ghRuleset.ts
6004
6033
  import { spawnSync as spawnSync5 } from "child_process";
@@ -6334,14 +6363,14 @@ function computeDesiredBypassActors(current, deployKeyId, flags) {
6334
6363
  }
6335
6364
 
6336
6365
  // src/lib/oteamConfig.ts
6337
- import { existsSync as existsSync8, readFileSync as readFileSync7, renameSync as renameSync2, writeFileSync as writeFileSync6 } from "fs";
6366
+ import { existsSync as existsSync8, readFileSync as readFileSync8, renameSync as renameSync2, writeFileSync as writeFileSync6 } from "fs";
6338
6367
  import { homedir } from "os";
6339
- import { join as join4 } from "path";
6340
- var OTEAM_CONFIG_PATH = join4(homedir(), ".open-team", "config.json");
6368
+ import { join as join5 } from "path";
6369
+ var OTEAM_CONFIG_PATH = join5(homedir(), ".open-team", "config.json");
6341
6370
  function readOteamConfig(configPath = OTEAM_CONFIG_PATH) {
6342
6371
  if (!existsSync8(configPath)) return null;
6343
6372
  try {
6344
- const parsed = JSON.parse(readFileSync7(configPath, "utf8"));
6373
+ const parsed = JSON.parse(readFileSync8(configPath, "utf8"));
6345
6374
  if (Array.isArray(parsed)) {
6346
6375
  throw new Error("config must be a JSON object, not an array");
6347
6376
  }
@@ -6356,7 +6385,7 @@ function patchStampHost(host, configPath = OTEAM_CONFIG_PATH) {
6356
6385
  let config2 = {};
6357
6386
  if (existsSync8(configPath)) {
6358
6387
  try {
6359
- const parsed = JSON.parse(readFileSync7(configPath, "utf8"));
6388
+ const parsed = JSON.parse(readFileSync8(configPath, "utf8"));
6360
6389
  if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
6361
6390
  config2 = parsed;
6362
6391
  }
@@ -6382,6 +6411,7 @@ function patchStampHost(host, configPath = OTEAM_CONFIG_PATH) {
6382
6411
  }
6383
6412
 
6384
6413
  // src/commands/init.ts
6414
+ var DEFAULT_ACTION_SOURCE = "OpenThinkAi/stamp-cli";
6385
6415
  function runInit(opts = {}) {
6386
6416
  maybePrintDeprecationNotice();
6387
6417
  const repoRoot = findRepoRoot();
@@ -6412,26 +6442,26 @@ For local-only / advisory use against this GitHub repo: re-run with \`stamp init
6412
6442
  if (!alreadyHasConfig) {
6413
6443
  if (opts.minimal) {
6414
6444
  writeFileSync7(configFile, stringifyConfig(MINIMAL_CONFIG));
6415
- writeFileSync7(join5(reviewersDir, "example.md"), EXAMPLE_REVIEWER_PROMPT);
6445
+ writeFileSync7(join6(reviewersDir, "example.md"), EXAMPLE_REVIEWER_PROMPT);
6416
6446
  } else {
6417
6447
  writeFileSync7(configFile, stringifyConfig(DEFAULT_CONFIG));
6418
6448
  writeFileSync7(
6419
- join5(reviewersDir, "security.md"),
6449
+ join6(reviewersDir, "security.md"),
6420
6450
  DEFAULT_SECURITY_PROMPT
6421
6451
  );
6422
6452
  writeFileSync7(
6423
- join5(reviewersDir, "standards.md"),
6453
+ join6(reviewersDir, "standards.md"),
6424
6454
  DEFAULT_STANDARDS_PROMPT
6425
6455
  );
6426
6456
  writeFileSync7(
6427
- join5(reviewersDir, "product.md"),
6457
+ join6(reviewersDir, "product.md"),
6428
6458
  DEFAULT_PRODUCT_PROMPT
6429
6459
  );
6430
6460
  }
6431
6461
  }
6432
6462
  const { keypair, created: keyCreated } = ensureUserKeypair();
6433
6463
  const userCfg = loadOrCreateUserConfig();
6434
- const pubKeyPath = join5(
6464
+ const pubKeyPath = join6(
6435
6465
  trustedKeysDir,
6436
6466
  publicKeyFingerprintFilename(keypair.fingerprint)
6437
6467
  );
@@ -6445,7 +6475,8 @@ For local-only / advisory use against this GitHub repo: re-run with \`stamp init
6445
6475
  const prCheckResult = maybeWriteVerifyWorkflow(
6446
6476
  repoRoot,
6447
6477
  opts.prCheck,
6448
- effectiveMode
6478
+ effectiveMode,
6479
+ opts.actionSource ?? DEFAULT_ACTION_SOURCE
6449
6480
  );
6450
6481
  const prModeResult = opts.prMode ? maybeWritePrModeMirrorWorkflow(repoRoot, { force: opts.prModeForce }) : { action: "skipped", path: PR_MODE_WORKFLOW_PATH, substitution: null };
6451
6482
  const agentsMdAction = opts.agentsMd === false ? "skipped" : ensureAgentsMd(repoRoot, effectiveMode);
@@ -6597,8 +6628,8 @@ function runBootstrapCommit(repoRoot, scaffoldOrSync) {
6597
6628
  return { kind: "skipped-already-tracked" };
6598
6629
  }
6599
6630
  const toAdd = [".stamp"];
6600
- if (existsSync9(join5(repoRoot, "AGENTS.md"))) toAdd.push("AGENTS.md");
6601
- if (existsSync9(join5(repoRoot, "CLAUDE.md"))) toAdd.push("CLAUDE.md");
6631
+ if (existsSync9(join6(repoRoot, "AGENTS.md"))) toAdd.push("AGENTS.md");
6632
+ if (existsSync9(join6(repoRoot, "CLAUDE.md"))) toAdd.push("CLAUDE.md");
6602
6633
  runGit(["add", ...toAdd], repoRoot);
6603
6634
  let hasStagedChanges = false;
6604
6635
  try {
@@ -6803,27 +6834,27 @@ function resolveMode(userMode, remoteClass) {
6803
6834
  }
6804
6835
  }
6805
6836
  var VERIFY_ACTION_REF = "v1.6.1";
6806
- function maybeWriteVerifyWorkflow(repoRoot, prCheckOpt, effectiveMode) {
6837
+ function maybeWriteVerifyWorkflow(repoRoot, prCheckOpt, effectiveMode, actionSource = DEFAULT_ACTION_SOURCE) {
6807
6838
  const path2 = ".github/workflows/stamp-verify.yml";
6808
- const fullPath = join5(repoRoot, path2);
6839
+ const fullPath = join6(repoRoot, path2);
6809
6840
  const defaultForMode = effectiveMode !== "server-gated";
6810
6841
  const shouldWrite = prCheckOpt ?? defaultForMode;
6811
6842
  if (!shouldWrite) return { action: "skipped", path: path2 };
6812
6843
  if (existsSync9(fullPath)) {
6813
6844
  return { action: "exists", path: path2 };
6814
6845
  }
6815
- ensureDir(dirname4(fullPath));
6816
- writeFileSync7(fullPath, renderVerifyWorkflow());
6846
+ ensureDir(dirname5(fullPath));
6847
+ writeFileSync7(fullPath, renderVerifyWorkflow(actionSource));
6817
6848
  return { action: "wrote", path: path2 };
6818
6849
  }
6819
6850
  var PR_MODE_WORKFLOW_PATH = ".github/workflows/stamp-mirror.yml";
6820
6851
  function maybeWritePrModeMirrorWorkflow(repoRoot, opts = {}) {
6821
- const fullPath = join5(repoRoot, PR_MODE_WORKFLOW_PATH);
6852
+ const fullPath = join6(repoRoot, PR_MODE_WORKFLOW_PATH);
6822
6853
  const substitution = derivePrModeSubstitution(repoRoot);
6823
6854
  if (existsSync9(fullPath) && !opts.force) {
6824
6855
  return { action: "exists", path: PR_MODE_WORKFLOW_PATH, substitution };
6825
6856
  }
6826
- ensureDir(dirname4(fullPath));
6857
+ ensureDir(dirname5(fullPath));
6827
6858
  writeFileSync7(fullPath, renderMirrorWorkflow(substitution));
6828
6859
  return { action: "wrote", path: PR_MODE_WORKFLOW_PATH, substitution };
6829
6860
  }
@@ -6831,9 +6862,9 @@ function derivePrModeSubstitution(repoRoot) {
6831
6862
  let host = null;
6832
6863
  let port = null;
6833
6864
  try {
6834
- const configFile = join5(repoRoot, ".stamp", "config.yml");
6865
+ const configFile = join6(repoRoot, ".stamp", "config.yml");
6835
6866
  if (existsSync9(configFile)) {
6836
- const cfg = parseConfigFromYaml(readFileSync8(configFile, "utf8"));
6867
+ const cfg = parseConfigFromYaml(readFileSync9(configFile, "utf8"));
6837
6868
  for (const rule of Object.values(cfg.branches)) {
6838
6869
  if (rule.review_server) {
6839
6870
  try {
@@ -7073,7 +7104,7 @@ function printPrModeWalkthrough(result) {
7073
7104
  );
7074
7105
  console.log();
7075
7106
  }
7076
- function renderVerifyWorkflow() {
7107
+ function renderVerifyWorkflow(actionSource = DEFAULT_ACTION_SOURCE) {
7077
7108
  return [
7078
7109
  "name: stamp verify",
7079
7110
  "",
@@ -7106,18 +7137,18 @@ function renderVerifyWorkflow() {
7106
7137
  " # would force per-step refetches.",
7107
7138
  " fetch-depth: 0",
7108
7139
  " - name: stamp/verify-attestation",
7109
- ` uses: OpenThinkAi/stamp-cli/.github/actions/verify-attestation@${VERIFY_ACTION_REF}`,
7140
+ ` uses: ${actionSource}/.github/actions/verify-attestation@${VERIFY_ACTION_REF}`,
7110
7141
  ""
7111
7142
  ].join("\n");
7112
7143
  }
7113
7144
 
7114
7145
  // src/commands/migrateServerAttested.ts
7115
- import { existsSync as existsSync10, readFileSync as readFileSync10, writeFileSync as writeFileSync8 } from "fs";
7116
- import { dirname as dirname5, join as join7, relative } from "path";
7146
+ import { existsSync as existsSync10, readFileSync as readFileSync11, writeFileSync as writeFileSync8 } from "fs";
7147
+ import { dirname as dirname6, join as join8, relative } from "path";
7117
7148
 
7118
7149
  // src/lib/migrateServerAttested.ts
7119
- import { readdirSync as readdirSync2, readFileSync as readFileSync9, statSync as statSync2 } from "fs";
7120
- import { join as join6 } from "path";
7150
+ import { readdirSync as readdirSync2, readFileSync as readFileSync10, statSync as statSync2 } from "fs";
7151
+ import { join as join7 } from "path";
7121
7152
  function detectExistingKeys(repoRoot, onSkip) {
7122
7153
  const dir = stampTrustedKeysDir(repoRoot);
7123
7154
  let entries;
@@ -7131,7 +7162,7 @@ function detectExistingKeys(repoRoot, onSkip) {
7131
7162
  const out = [];
7132
7163
  for (const filename of entries.sort()) {
7133
7164
  if (!filename.endsWith(".pub")) continue;
7134
- const full = join6(dir, filename);
7165
+ const full = join7(dir, filename);
7135
7166
  try {
7136
7167
  const st = statSync2(full);
7137
7168
  if (!st.isFile()) continue;
@@ -7140,7 +7171,7 @@ function detectExistingKeys(repoRoot, onSkip) {
7140
7171
  }
7141
7172
  let pem;
7142
7173
  try {
7143
- pem = readFileSync9(full, "utf8");
7174
+ pem = readFileSync10(full, "utf8");
7144
7175
  } catch (err) {
7145
7176
  onSkip?.(filename, err instanceof Error ? err.message : String(err));
7146
7177
  continue;
@@ -7328,7 +7359,7 @@ function runMigrateToServerAttested(opts = {}) {
7328
7359
  console.error(`note: skipping .stamp/trusted-keys/${filename} \u2014 ${reason}`);
7329
7360
  })
7330
7361
  );
7331
- const manifestPath = join7(repoRoot, MANIFEST_RELATIVE_PATH);
7362
+ const manifestPath = join8(repoRoot, MANIFEST_RELATIVE_PATH);
7332
7363
  const manifestExists = existsSync10(manifestPath);
7333
7364
  let adminFingerprints;
7334
7365
  if (manifestExists) {
@@ -7361,7 +7392,7 @@ function runMigrateToServerAttested(opts = {}) {
7361
7392
  `expected .stamp/config.yml at ${cfgPath} but found none. Run \`stamp init\` first.`
7362
7393
  );
7363
7394
  }
7364
- const cfgInput = readFileSync10(cfgPath, "utf8");
7395
+ const cfgInput = readFileSync11(cfgPath, "utf8");
7365
7396
  const rewrite = rewriteConfigForMigration(cfgInput);
7366
7397
  if (dryRun) {
7367
7398
  printDryRun({
@@ -7385,10 +7416,10 @@ function runMigrateToServerAttested(opts = {}) {
7385
7416
  console.error(`warning: ${w}`);
7386
7417
  }
7387
7418
  let manifestAction;
7388
- if (manifestExists && readFileSync10(manifestPath, "utf8") === manifestText) {
7419
+ if (manifestExists && readFileSync11(manifestPath, "utf8") === manifestText) {
7389
7420
  manifestAction = "unchanged";
7390
7421
  } else {
7391
- ensureDir(dirname5(manifestPath));
7422
+ ensureDir(dirname6(manifestPath));
7392
7423
  writeFileSync8(manifestPath, manifestText);
7393
7424
  manifestAction = "wrote";
7394
7425
  }
@@ -7454,7 +7485,7 @@ function readExistingAdminFingerprints(manifestPath) {
7454
7485
  const out = /* @__PURE__ */ new Set();
7455
7486
  let text;
7456
7487
  try {
7457
- text = readFileSync10(manifestPath, "utf8");
7488
+ text = readFileSync11(manifestPath, "utf8");
7458
7489
  } catch {
7459
7490
  return out;
7460
7491
  }
@@ -7529,9 +7560,9 @@ function indent(text, prefix) {
7529
7560
  import { spawnSync as spawnSync6 } from "child_process";
7530
7561
  import { request as httpRequest } from "http";
7531
7562
  import { request as httpsRequest } from "https";
7532
- import { existsSync as existsSync11, readFileSync as readFileSync11 } from "fs";
7563
+ import { existsSync as existsSync11, readFileSync as readFileSync12 } from "fs";
7533
7564
  import { homedir as homedir2, hostname, userInfo } from "os";
7534
- import { join as join8 } from "path";
7565
+ import { join as join9 } from "path";
7535
7566
  import { createInterface as createInterface2 } from "readline/promises";
7536
7567
 
7537
7568
  // src/lib/inviteUrl.ts
@@ -7698,10 +7729,10 @@ function runInvitesMint(opts) {
7698
7729
  }
7699
7730
  }
7700
7731
  function defaultSshPubkeyPath() {
7701
- return join8(homedir2(), ".ssh", "id_ed25519.pub");
7732
+ return join9(homedir2(), ".ssh", "id_ed25519.pub");
7702
7733
  }
7703
7734
  function defaultStampPubkeyPath() {
7704
- return join8(homedir2(), ".stamp", "keys", "ed25519.pub");
7735
+ return join9(homedir2(), ".stamp", "keys", "ed25519.pub");
7705
7736
  }
7706
7737
  function defaultShortName() {
7707
7738
  const u = userInfo().username || "user";
@@ -7714,13 +7745,13 @@ function loadSshPubkey(path2) {
7714
7745
  `SSH pubkey not found at ${path2}. Generate one with \`ssh-keygen -t ed25519\` or pass --ssh-pubkey <path>.`
7715
7746
  );
7716
7747
  }
7717
- const raw = readFileSync11(path2, "utf8");
7748
+ const raw = readFileSync12(path2, "utf8");
7718
7749
  const parsed = parseSshPubkey(raw);
7719
7750
  return { path: path2, full: parsed.full, fingerprint: parsed.fingerprint };
7720
7751
  }
7721
7752
  function loadStampPubkey(path2) {
7722
7753
  if (!existsSync11(path2)) return null;
7723
- const pem = readFileSync11(path2, "utf8");
7754
+ const pem = readFileSync12(path2, "utf8");
7724
7755
  try {
7725
7756
  const fp = fingerprintFromPem(pem);
7726
7757
  return { path: path2, pem, fingerprint: fp };
@@ -7865,15 +7896,15 @@ async function runInvitesAccept(opts) {
7865
7896
 
7866
7897
  // src/commands/provision.ts
7867
7898
  import { spawnSync as spawnSync8 } from "child_process";
7868
- import { existsSync as existsSync13, mkdtempSync, readFileSync as readFileSync12, rmSync, writeFileSync as writeFileSync10 } from "fs";
7899
+ import { existsSync as existsSync13, mkdtempSync, readFileSync as readFileSync13, rmSync, writeFileSync as writeFileSync10 } from "fs";
7869
7900
  import { tmpdir } from "os";
7870
- import { join as join9, resolve as resolvePath } from "path";
7901
+ import { join as join10, resolve as resolvePath } from "path";
7871
7902
  import { parse as parseYaml8 } from "yaml";
7872
7903
 
7873
7904
  // src/commands/server.ts
7874
7905
  import { spawnSync as spawnSync7 } from "child_process";
7875
7906
  import { existsSync as existsSync12, mkdirSync as mkdirSync4, renameSync as renameSync3, unlinkSync as unlinkSync2, writeFileSync as writeFileSync9 } from "fs";
7876
- import { dirname as dirname6 } from "path";
7907
+ import { dirname as dirname7 } from "path";
7877
7908
  import { stringify as stringifyYaml2 } from "yaml";
7878
7909
 
7879
7910
  // src/lib/perRepoKey.ts
@@ -8032,7 +8063,7 @@ function writeConfig(opts) {
8032
8063
  repoRootPrefix: opts.repoRootPrefix
8033
8064
  });
8034
8065
  const path2 = userServerConfigPath();
8035
- const dir = dirname6(path2);
8066
+ const dir = dirname7(path2);
8036
8067
  if (!existsSync12(dir)) mkdirSync4(dir, { recursive: true, mode: 448 });
8037
8068
  const tmp = `${path2}.tmp.${process.pid}`;
8038
8069
  writeFileSync9(tmp, yaml, { mode: 384 });
@@ -8345,9 +8376,9 @@ async function runMigrateExisting(opts, server2) {
8345
8376
  console.log("\n(dry run \u2014 no changes made)");
8346
8377
  return;
8347
8378
  }
8348
- const stagingDir = mkdtempSync(join9(tmpdir(), "stamp-migrate-"));
8349
- const bareCloneDir = join9(stagingDir, `${opts.name}.git`);
8350
- const tarballPath = join9(stagingDir, `${opts.name}.tar.gz`);
8379
+ const stagingDir = mkdtempSync(join10(tmpdir(), "stamp-migrate-"));
8380
+ const bareCloneDir = join10(stagingDir, `${opts.name}.git`);
8381
+ const tarballPath = join10(stagingDir, `${opts.name}.tar.gz`);
8351
8382
  try {
8352
8383
  console.log(`
8353
8384
  Building bare-clone tarball of existing repo`);
@@ -8383,9 +8414,9 @@ function ensureCwdIsGitRepo(cwd) {
8383
8414
  }
8384
8415
  }
8385
8416
  function ensureStampInitDone(cwd) {
8386
- if (!existsSync13(join9(cwd, ".stamp", "config.yml"))) {
8417
+ if (!existsSync13(join10(cwd, ".stamp", "config.yml"))) {
8387
8418
  throw new Error(
8388
- `--migrate-existing expects this repo to already be stamp-init'd (${join9(cwd, ".stamp/config.yml")} not found). Run \`stamp init --mode local-only\` first, calibrate your reviewers, then re-run with --migrate-existing.`
8419
+ `--migrate-existing expects this repo to already be stamp-init'd (${join10(cwd, ".stamp/config.yml")} not found). Run \`stamp init --mode local-only\` first, calibrate your reviewers, then re-run with --migrate-existing.`
8389
8420
  );
8390
8421
  }
8391
8422
  }
@@ -8510,7 +8541,7 @@ mirror.yml was added to .stamp/. Commit it through the normal stamp flow:`);
8510
8541
  }
8511
8542
  }
8512
8543
  function readMirrorYmlGithubRepo(repoRoot) {
8513
- const path2 = join9(repoRoot, ".stamp", "mirror.yml");
8544
+ const path2 = join10(repoRoot, ".stamp", "mirror.yml");
8514
8545
  if (!existsSync13(path2)) {
8515
8546
  throw new Error(
8516
8547
  `${path2} not found \u2014 --migrate-bypass operates on an already-server-gated repo, but this cwd has no .stamp/mirror.yml. If the repo is not yet server-gated, provision it first with \`stamp provision --migrate-existing\`.`
@@ -8518,7 +8549,7 @@ function readMirrorYmlGithubRepo(repoRoot) {
8518
8549
  }
8519
8550
  let raw;
8520
8551
  try {
8521
- raw = readFileSync12(path2, "utf8");
8552
+ raw = readFileSync13(path2, "utf8");
8522
8553
  } catch (err) {
8523
8554
  throw new Error(
8524
8555
  `could not read ${path2}: ${err instanceof Error ? err.message : String(err)}`
@@ -8758,10 +8789,10 @@ import {
8758
8789
  import {
8759
8790
  existsSync as existsSync14,
8760
8791
  readdirSync as readdirSync3,
8761
- readFileSync as readFileSync13,
8792
+ readFileSync as readFileSync14,
8762
8793
  writeFileSync as writeFileSync11
8763
8794
  } from "fs";
8764
- import { join as join10, resolve } from "path";
8795
+ import { join as join11, resolve } from "path";
8765
8796
  var USERS_EXIT = {
8766
8797
  OK: 0,
8767
8798
  CONFIG: 1,
@@ -8820,10 +8851,10 @@ function findExistingTrustedKey(repoRoot, fingerprint) {
8820
8851
  if (!existsSync14(dir)) return null;
8821
8852
  for (const f of readdirSync3(dir)) {
8822
8853
  if (!f.endsWith(".pub")) continue;
8823
- const fullPath = join10(dir, f);
8854
+ const fullPath = join11(dir, f);
8824
8855
  let pem;
8825
8856
  try {
8826
- pem = readFileSync13(fullPath, "utf8");
8857
+ pem = readFileSync14(fullPath, "utf8");
8827
8858
  } catch {
8828
8859
  continue;
8829
8860
  }
@@ -8875,7 +8906,7 @@ function runTrustGrant(opts) {
8875
8906
  );
8876
8907
  }
8877
8908
  const repoRoot = resolve(opts.repoPath ?? process.cwd());
8878
- if (!existsSync14(join10(repoRoot, ".git"))) {
8909
+ if (!existsSync14(join11(repoRoot, ".git"))) {
8879
8910
  throw new UsageError(`${repoRoot} is not a git repository`);
8880
8911
  }
8881
8912
  if (!existsSync14(stampConfigDir(repoRoot))) {
@@ -8917,7 +8948,7 @@ function runTrustGrant(opts) {
8917
8948
  runGit2(["checkout", "-b", branch], repoRoot);
8918
8949
  const keysDir = stampTrustedKeysDir(repoRoot);
8919
8950
  ensureDir(keysDir, 493);
8920
- const keyFile = join10(keysDir, `${opts.shortName}.pub`);
8951
+ const keyFile = join11(keysDir, `${opts.shortName}.pub`);
8921
8952
  writeFileSync11(keyFile, pem, { mode: 420 });
8922
8953
  runGit2(["add", keyFile], repoRoot);
8923
8954
  runGit2(
@@ -9115,8 +9146,8 @@ function runUsersRemove(opts) {
9115
9146
  }
9116
9147
 
9117
9148
  // src/commands/keys.ts
9118
- import { existsSync as existsSync15, readdirSync as readdirSync4, readFileSync as readFileSync14, writeFileSync as writeFileSync12 } from "fs";
9119
- import { basename, join as join11 } from "path";
9149
+ import { existsSync as existsSync15, readdirSync as readdirSync4, readFileSync as readFileSync15, writeFileSync as writeFileSync12 } from "fs";
9150
+ import { basename, join as join12 } from "path";
9120
9151
  function keysGenerate() {
9121
9152
  const existing = loadUserKeypair();
9122
9153
  if (existing) {
@@ -9160,7 +9191,7 @@ function keysList() {
9160
9191
  }
9161
9192
  for (const file of pubFiles.sort()) {
9162
9193
  try {
9163
- const pem = readFileSync14(join11(trustedDir, file), "utf8");
9194
+ const pem = readFileSync15(join12(trustedDir, file), "utf8");
9164
9195
  const fp = fingerprintFromPem(pem);
9165
9196
  const marker = local && fp === local.fingerprint ? " (you)" : "";
9166
9197
  console.log(` ${fp}${marker} [${file}]`);
@@ -9187,7 +9218,7 @@ function keysTrust(pubFile) {
9187
9218
  if (!existsSync15(pubFile)) {
9188
9219
  throw new Error(`public key file not found: ${pubFile}`);
9189
9220
  }
9190
- const pem = readFileSync14(pubFile, "utf8");
9221
+ const pem = readFileSync15(pubFile, "utf8");
9191
9222
  let fingerprint;
9192
9223
  try {
9193
9224
  fingerprint = fingerprintFromPem(pem);
@@ -9197,7 +9228,7 @@ function keysTrust(pubFile) {
9197
9228
  );
9198
9229
  }
9199
9230
  const filename = publicKeyFingerprintFilename(fingerprint);
9200
- const dest = join11(trustedDir, filename);
9231
+ const dest = join12(trustedDir, filename);
9201
9232
  if (existsSync15(dest)) {
9202
9233
  console.log(`${fingerprint} is already trusted (${basename(dest)})`);
9203
9234
  return;
@@ -9657,14 +9688,14 @@ function gitDiffBetween(repoRoot, base, head) {
9657
9688
 
9658
9689
  // src/commands/adminRotate.ts
9659
9690
  import { execFileSync as execFileSync6 } from "child_process";
9660
- import { copyFileSync, existsSync as existsSync16, readFileSync as readFileSync15, writeFileSync as writeFileSync13 } from "fs";
9661
- import { join as join12, relative as relative2 } from "path";
9691
+ import { copyFileSync, existsSync as existsSync16, readFileSync as readFileSync16, writeFileSync as writeFileSync13 } from "fs";
9692
+ import { join as join13, relative as relative2 } from "path";
9662
9693
  var NAME_PATTERN2 = /^[A-Za-z0-9_.-]+$/;
9663
9694
  var FINGERPRINT_PATTERN2 = /^sha256:[0-9a-f]{64}$/;
9664
9695
  var KNOWN_CAPABILITIES2 = ["admin", "operator", "server"];
9665
9696
  function runAdminAddKey(opts) {
9666
9697
  const repoRoot = findRepoRoot();
9667
- const manifestPath = join12(repoRoot, MANIFEST_RELATIVE_PATH);
9698
+ const manifestPath = join13(repoRoot, MANIFEST_RELATIVE_PATH);
9668
9699
  const trustedDir = stampTrustedKeysDir(repoRoot);
9669
9700
  if (!NAME_PATTERN2.test(opts.name)) {
9670
9701
  throw new Error(
@@ -9675,7 +9706,7 @@ function runAdminAddKey(opts) {
9675
9706
  if (!existsSync16(opts.pubkeyPath)) {
9676
9707
  throw new Error(`pubkey file not found: ${opts.pubkeyPath}`);
9677
9708
  }
9678
- const pem = readFileSync15(opts.pubkeyPath, "utf8");
9709
+ const pem = readFileSync16(opts.pubkeyPath, "utf8");
9679
9710
  if (!pem.includes("-----BEGIN PUBLIC KEY-----")) {
9680
9711
  throw new Error(
9681
9712
  `${opts.pubkeyPath} does not look like a public key PEM (no "-----BEGIN PUBLIC KEY-----" header). Refusing to import \u2014 did you accidentally pass the private key path? Public keys live at ~/.stamp/keys/ed25519.pub (note the .pub suffix).`
@@ -9694,7 +9725,7 @@ function runAdminAddKey(opts) {
9694
9725
  `no manifest at ${MANIFEST_RELATIVE_PATH}. Run \`stamp init --migrate-to-server-attested\` to bootstrap one, then re-run \`stamp admin add-key\`.`
9695
9726
  );
9696
9727
  }
9697
- const existing = parseManifest(readFileSync15(manifestPath, "utf8"));
9728
+ const existing = parseManifest(readFileSync16(manifestPath, "utf8"));
9698
9729
  if (!existing) {
9699
9730
  throw new Error(
9700
9731
  `${MANIFEST_RELATIVE_PATH} failed to parse. Fix the file (see \`stamp admin list-keys\` for the current state) before adding a new key.`
@@ -9729,7 +9760,7 @@ function runAdminAddKey(opts) {
9729
9760
  }
9730
9761
  writeFileSync13(manifestPath, yamlText);
9731
9762
  const pubDestFilename = publicKeyFingerprintFilename(fingerprint);
9732
- const pubDestPath = join12(trustedDir, pubDestFilename);
9763
+ const pubDestPath = join13(trustedDir, pubDestFilename);
9733
9764
  if (!existsSync16(pubDestPath)) {
9734
9765
  copyFileSync(opts.pubkeyPath, pubDestPath);
9735
9766
  }
@@ -9754,7 +9785,7 @@ function runAdminAddKey(opts) {
9754
9785
  }
9755
9786
  function runAdminRevoke(opts) {
9756
9787
  const repoRoot = findRepoRoot();
9757
- const manifestPath = join12(repoRoot, MANIFEST_RELATIVE_PATH);
9788
+ const manifestPath = join13(repoRoot, MANIFEST_RELATIVE_PATH);
9758
9789
  if (!FINGERPRINT_PATTERN2.test(opts.fingerprint)) {
9759
9790
  throw new Error(
9760
9791
  `fingerprint ${JSON.stringify(opts.fingerprint)} is not in the expected sha256:<64-hex> form. Run \`stamp admin list-keys\` to copy a fingerprint exactly.`
@@ -9765,7 +9796,7 @@ function runAdminRevoke(opts) {
9765
9796
  `no manifest at ${MANIFEST_RELATIVE_PATH} \u2014 nothing to revoke.`
9766
9797
  );
9767
9798
  }
9768
- const existing = parseManifest(readFileSync15(manifestPath, "utf8"));
9799
+ const existing = parseManifest(readFileSync16(manifestPath, "utf8"));
9769
9800
  if (!existing) {
9770
9801
  throw new Error(
9771
9802
  `${MANIFEST_RELATIVE_PATH} failed to parse. Fix the file before revoking.`
@@ -9826,7 +9857,7 @@ function runAdminRevoke(opts) {
9826
9857
  }
9827
9858
  function runAdminListKeys(opts = {}) {
9828
9859
  const repoRoot = findRepoRoot();
9829
- const manifestPath = join12(repoRoot, MANIFEST_RELATIVE_PATH);
9860
+ const manifestPath = join13(repoRoot, MANIFEST_RELATIVE_PATH);
9830
9861
  if (!existsSync16(manifestPath)) {
9831
9862
  if (opts.json) {
9832
9863
  process.stdout.write(JSON.stringify({ entries: [] }, null, 2) + "\n");
@@ -9838,7 +9869,7 @@ function runAdminListKeys(opts = {}) {
9838
9869
  );
9839
9870
  return;
9840
9871
  }
9841
- const manifest = parseManifest(readFileSync15(manifestPath, "utf8"));
9872
+ const manifest = parseManifest(readFileSync16(manifestPath, "utf8"));
9842
9873
  if (!manifest) {
9843
9874
  throw new Error(
9844
9875
  `${MANIFEST_RELATIVE_PATH} failed to parse. Fix the file by hand (see \`git show HEAD:${MANIFEST_RELATIVE_PATH}\` for the last good version) before continuing.`
@@ -10613,7 +10644,7 @@ function readReviewerSource2(reviewerName, repoRoot) {
10613
10644
 
10614
10645
  // src/commands/prune.ts
10615
10646
  import { existsSync as existsSync18, readdirSync as readdirSync5, statSync as statSync3, unlinkSync as unlinkSync3 } from "fs";
10616
- import { join as join13 } from "path";
10647
+ import { join as join14 } from "path";
10617
10648
 
10618
10649
  // src/lib/duration.ts
10619
10650
  function parseRetentionDuration(input) {
@@ -10642,8 +10673,8 @@ function runPrune(opts) {
10642
10673
  );
10643
10674
  const repoRoot = findRepoRoot();
10644
10675
  const dbPath = stampStateDbPath(repoRoot);
10645
- const parsesDir = join13(gitCommonDir(repoRoot), "stamp", "failed-parses");
10646
- const runsDir = join13(gitCommonDir(repoRoot), "stamp", "failed-runs");
10676
+ const parsesDir = join14(gitCommonDir(repoRoot), "stamp", "failed-parses");
10677
+ const runsDir = join14(gitCommonDir(repoRoot), "stamp", "failed-runs");
10647
10678
  const spoolCutoffMs = Date.now() - durationMs;
10648
10679
  if (!existsSync18(dbPath) && !existsSync18(parsesDir) && !existsSync18(runsDir)) {
10649
10680
  console.log(
@@ -10724,7 +10755,7 @@ function peekSpools(spoolDir, cutoffMs) {
10724
10755
  if (!existsSync18(spoolDir)) return [];
10725
10756
  const out = [];
10726
10757
  for (const entry of readdirSync5(spoolDir)) {
10727
- const filepath = join13(spoolDir, entry);
10758
+ const filepath = join14(spoolDir, entry);
10728
10759
  let stat;
10729
10760
  try {
10730
10761
  stat = statSync3(filepath);
@@ -10879,27 +10910,27 @@ function loadOrEmpty() {
10879
10910
  import { spawnSync as spawnSync15 } from "child_process";
10880
10911
  import {
10881
10912
  existsSync as existsSync21,
10882
- readFileSync as readFileSync17,
10913
+ readFileSync as readFileSync18,
10883
10914
  statSync as statSync4,
10884
10915
  unlinkSync as unlinkSync4,
10885
10916
  writeFileSync as writeFileSync15
10886
10917
  } from "fs";
10887
- import { join as join15, relative as relative3, resolve as resolve2 } from "path";
10918
+ import { join as join16, relative as relative3, resolve as resolve2 } from "path";
10888
10919
  import { parse as parseYaml10, stringify as stringifyYaml3 } from "yaml";
10889
10920
 
10890
10921
  // src/lib/reviewerLock.ts
10891
- import { existsSync as existsSync20, readFileSync as readFileSync16, writeFileSync as writeFileSync14 } from "fs";
10892
- import { join as join14 } from "path";
10922
+ import { existsSync as existsSync20, readFileSync as readFileSync17, writeFileSync as writeFileSync14 } from "fs";
10923
+ import { join as join15 } from "path";
10893
10924
  var LOCK_FILE_VERSION = 1;
10894
10925
  var LOCK_DRIFT_EXIT = 3;
10895
10926
  function lockFilePath(repoRoot, reviewerName) {
10896
- return join14(repoRoot, ".stamp", "reviewers", `${reviewerName}.lock.json`);
10927
+ return join15(repoRoot, ".stamp", "reviewers", `${reviewerName}.lock.json`);
10897
10928
  }
10898
10929
  function readLockFile(repoRoot, reviewerName) {
10899
10930
  const path2 = lockFilePath(repoRoot, reviewerName);
10900
10931
  if (!existsSync20(path2)) return null;
10901
10932
  try {
10902
- const raw = readFileSync16(path2, "utf8");
10933
+ const raw = readFileSync17(path2, "utf8");
10903
10934
  const parsed = JSON.parse(raw);
10904
10935
  if (typeof parsed.version !== "number" || typeof parsed.source !== "string" || typeof parsed.ref !== "string" || typeof parsed.reviewer !== "string" || typeof parsed.prompt_sha256 !== "string" || typeof parsed.tools_sha256 !== "string" || typeof parsed.mcp_sha256 !== "string") {
10905
10936
  throw new Error(`malformed lock file at ${path2}`);
@@ -10920,13 +10951,13 @@ function checkReviewerDrift(repoRoot, reviewerName, def) {
10920
10951
  if (!lock) {
10921
10952
  return unpinnedResult();
10922
10953
  }
10923
- const promptPath = join14(repoRoot, def.prompt);
10954
+ const promptPath = join15(repoRoot, def.prompt);
10924
10955
  if (!existsSync20(promptPath)) {
10925
10956
  throw new Error(
10926
10957
  `reviewer "${reviewerName}" has a lock file but its prompt "${def.prompt}" does not exist on disk. Re-run 'stamp reviewers fetch ${reviewerName} --from ${lock.source}@${lock.ref}' to restore it, or delete the lock file to un-pin the reviewer.`
10927
10958
  );
10928
10959
  }
10929
- const promptBytes = readFileSync16(promptPath);
10960
+ const promptBytes = readFileSync17(promptPath);
10930
10961
  const observedPrompt = hashPromptBytes(promptBytes);
10931
10962
  const observedTools = hashTools(def.tools);
10932
10963
  const observedMcp = hashMcpServers(def.mcp_servers);
@@ -11128,8 +11159,8 @@ async function reviewersTest(name, diff) {
11128
11159
  console.log(` prompt sourced from working tree (test/iteration use case)`);
11129
11160
  console.log();
11130
11161
  const def = config2.reviewers[name];
11131
- const promptPath = join15(repoRoot, def.prompt);
11132
- const systemPrompt = readFileSync17(promptPath, "utf8");
11162
+ const promptPath = join16(repoRoot, def.prompt);
11163
+ const systemPrompt = readFileSync18(promptPath, "utf8");
11133
11164
  const result = await invokeReviewer({
11134
11165
  reviewer: name,
11135
11166
  config: config2,
@@ -11267,7 +11298,7 @@ async function reviewersFetch(reviewerName, opts) {
11267
11298
  mcpServers = validateMcpServersFromSource(parsed.mcp_servers, source, ref);
11268
11299
  }
11269
11300
  }
11270
- const promptPath = join15(reviewersDir, `${reviewerName}.md`);
11301
+ const promptPath = join16(reviewersDir, `${reviewerName}.md`);
11271
11302
  const promptBytes = Buffer.from(promptText, "utf8");
11272
11303
  const promptSha = hashPromptBytes(promptBytes);
11273
11304
  const toolsSha = hashTools(tools);
@@ -11605,28 +11636,6 @@ function printGate(result, base_sha, head_sha) {
11605
11636
 
11606
11637
  // src/commands/update.ts
11607
11638
  import { spawnSync as spawnSync16 } from "child_process";
11608
-
11609
- // src/lib/version.ts
11610
- import { readFileSync as readFileSync18 } from "fs";
11611
- import { dirname as dirname7, join as join16 } from "path";
11612
- import { fileURLToPath as fileURLToPath2 } from "url";
11613
- function readPackageVersion() {
11614
- const here = dirname7(fileURLToPath2(import.meta.url));
11615
- for (let dir = here, i = 0; i < 6; i++) {
11616
- try {
11617
- const raw = readFileSync18(join16(dir, "package.json"), "utf8");
11618
- const pkg = JSON.parse(raw);
11619
- if (pkg.name === "@openthink/stamp" && pkg.version) return pkg.version;
11620
- } catch {
11621
- }
11622
- const parent = dirname7(dir);
11623
- if (parent === dir) break;
11624
- dir = parent;
11625
- }
11626
- throw new Error("could not locate @openthink/stamp package.json to read version");
11627
- }
11628
-
11629
- // src/commands/update.ts
11630
11639
  var PKG_NAME = "@openthink/stamp";
11631
11640
  function compareSemver(a, b) {
11632
11641
  const parse2 = (v) => (v.split("-")[0] ?? "0.0.0").split(".").map((n) => parseInt(n, 10) || 0);
@@ -12228,6 +12237,9 @@ program.command("init").description(
12228
12237
  ).option(
12229
12238
  "--pr-mode-force",
12230
12239
  "with --pr-mode, overwrite an existing .github/workflows/stamp-mirror.yml (useful when re-running after configuring review_server so the host/port placeholders fill in)"
12240
+ ).option(
12241
+ "--action-source <org/repo>",
12242
+ "GitHub repo that hosts the stamp/verify-attestation action used by .github/workflows/stamp-verify.yml. Default 'OpenThinkAi/stamp-cli'. Override when consuming a fork (e.g. 'Anglepoint-Inc/anglepoint-stamp-server') so the workflow tracks your fork's updates instead of the upstream."
12231
12243
  ).action(
12232
12244
  (opts) => {
12233
12245
  try {
@@ -12235,6 +12247,11 @@ program.command("init").description(
12235
12247
  runMigrateToServerAttested({ dryRun: opts.dryRun === true });
12236
12248
  return;
12237
12249
  }
12250
+ if (opts.dryRun === true) {
12251
+ throw new Error(
12252
+ "--dry-run is supported only with --migrate-to-server-attested. Re-run with --migrate-to-server-attested to preview the migration scaffold, or drop --dry-run to run the requested init operation."
12253
+ );
12254
+ }
12238
12255
  let mode;
12239
12256
  if (opts.mode === void 0) {
12240
12257
  mode = void 0;
@@ -12245,6 +12262,13 @@ program.command("init").description(
12245
12262
  `--mode must be 'server-gated' or 'local-only' (got "${opts.mode}")`
12246
12263
  );
12247
12264
  }
12265
+ if (opts.actionSource !== void 0) {
12266
+ if (!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(opts.actionSource)) {
12267
+ throw new Error(
12268
+ `--action-source must be of the form 'org/repo' (got "${opts.actionSource}")`
12269
+ );
12270
+ }
12271
+ }
12248
12272
  runInit({
12249
12273
  minimal: opts.minimal,
12250
12274
  agentsMd: opts.agentsMd,
@@ -12260,7 +12284,8 @@ program.command("init").description(
12260
12284
  // default fires when the operator hasn't opted out.
12261
12285
  prCheck: opts.prCheck === false ? false : void 0,
12262
12286
  prMode: opts.prMode === true,
12263
- prModeForce: opts.prModeForce === true
12287
+ prModeForce: opts.prModeForce === true,
12288
+ actionSource: opts.actionSource
12264
12289
  });
12265
12290
  } catch (err) {
12266
12291
  const message = err instanceof Error ? err.message : String(err);