opencode-skills-deck 0.2.0 → 0.3.0

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/tui.js CHANGED
@@ -5089,7 +5089,7 @@ var recursive = /* @__PURE__ */ new WeakMap();
5089
5089
  var NONE = 0;
5090
5090
  var ASSUMED = 1;
5091
5091
  var PROVEN = 2;
5092
- function isRecursive(inst, stack, resolve2) {
5092
+ function isRecursive(inst, stack, resolve3) {
5093
5093
  const cached2 = recursive.get(inst);
5094
5094
  if (cached2 !== void 0)
5095
5095
  return cached2 ? PROVEN : NONE;
@@ -5099,7 +5099,7 @@ function isRecursive(inst, stack, resolve2) {
5099
5099
  let result = NONE;
5100
5100
  const check2 = (child) => {
5101
5101
  if (result !== PROVEN && child?._zod) {
5102
- const answer = isRecursive(child, stack, resolve2);
5102
+ const answer = isRecursive(child, stack, resolve3);
5103
5103
  if (answer > result)
5104
5104
  result = answer;
5105
5105
  }
@@ -5110,7 +5110,7 @@ function isRecursive(inst, stack, resolve2) {
5110
5110
  const desc = Object.getOwnPropertyDescriptor(sh, key);
5111
5111
  if (spread && !desc.enumerable)
5112
5112
  continue;
5113
- const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve2) : NONE;
5113
+ const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve3) : NONE;
5114
5114
  if (child > answer)
5115
5115
  answer = child;
5116
5116
  }
@@ -5174,7 +5174,7 @@ function isRecursive(inst, stack, resolve2) {
5174
5174
  break;
5175
5175
  // `$ZodLazy` caches its inner on the def, so a resolved edge is followed exactly
5176
5176
  case "lazy": {
5177
- const inner = def._cachedInner ?? (resolve2 ? inst._zod.innerType : void 0);
5177
+ const inner = def._cachedInner ?? (resolve3 ? inst._zod.innerType : void 0);
5178
5178
  merge2(inner ? isRecursive(inner, stack, false) : ASSUMED);
5179
5179
  break;
5180
5180
  }
@@ -19790,16 +19790,36 @@ function normalizeOpenKey(value) {
19790
19790
  // packages/core/src/exec.ts
19791
19791
  import { spawn } from "node:child_process";
19792
19792
  var MAX_BUFFER = 16 * 1024 * 1024;
19793
- function createRunner() {
19794
- return (command, args, options) => new Promise((resolve2, reject) => {
19793
+ var DEFAULT_TIMEOUT_MS = 3e4;
19794
+ function createRunner(options = {}) {
19795
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
19796
+ return (command, args, options2) => new Promise((resolve3, reject) => {
19795
19797
  const child = spawn(command, args, {
19796
- cwd: options?.cwd,
19798
+ cwd: options2?.cwd,
19797
19799
  env: process.env,
19798
19800
  stdio: ["ignore", "pipe", "pipe"]
19799
19801
  });
19800
19802
  let stdout = "";
19801
19803
  let stderr = "";
19802
19804
  let overflow = false;
19805
+ let settled = false;
19806
+ const fail = (error62) => {
19807
+ if (settled) return;
19808
+ settled = true;
19809
+ clearTimeout(timer);
19810
+ reject(error62);
19811
+ };
19812
+ const succeed = (result) => {
19813
+ if (settled) return;
19814
+ settled = true;
19815
+ clearTimeout(timer);
19816
+ resolve3(result);
19817
+ };
19818
+ const timer = setTimeout(() => {
19819
+ child.kill("SIGTERM");
19820
+ fail(new Error(`${command} ${args.join(" ")} timed out after ${timeoutMs}ms`));
19821
+ }, timeoutMs);
19822
+ timer.unref?.();
19803
19823
  child.stdout?.setEncoding("utf8");
19804
19824
  child.stderr?.setEncoding("utf8");
19805
19825
  child.stdout?.on("data", (chunk) => {
@@ -19819,19 +19839,20 @@ function createRunner() {
19819
19839
  }
19820
19840
  });
19821
19841
  child.on("error", (error62) => {
19822
- reject(new Error(`${command} ${args.join(" ")} failed: ${error62.message}`));
19842
+ fail(new Error(`${command} ${args.join(" ")} failed: ${error62.message}`));
19823
19843
  });
19824
19844
  child.on("close", (code) => {
19845
+ if (settled) return;
19825
19846
  if (overflow) {
19826
- reject(new Error(`${command} ${args.join(" ")} failed: \u51FA\u529B\u304C\u5927\u304D\u3059\u304E\u307E\u3059`));
19847
+ fail(new Error(`${command} ${args.join(" ")} failed: \u51FA\u529B\u304C\u5927\u304D\u3059\u304E\u307E\u3059`));
19827
19848
  return;
19828
19849
  }
19829
19850
  if (code === 0) {
19830
- resolve2({ stdout, stderr });
19851
+ succeed({ stdout, stderr });
19831
19852
  return;
19832
19853
  }
19833
19854
  const detail = stderr.trim() || `exit code ${code ?? "unknown"}`;
19834
- reject(new Error(`${command} ${args.join(" ")} failed: ${detail}`));
19855
+ fail(new Error(`${command} ${args.join(" ")} failed: ${detail}`));
19835
19856
  });
19836
19857
  });
19837
19858
  }
@@ -19879,7 +19900,7 @@ function resolvePaths(options = {}) {
19879
19900
 
19880
19901
  // packages/core/src/repo.ts
19881
19902
  import { execFileSync } from "node:child_process";
19882
- import { join as join3 } from "node:path";
19903
+ import { dirname, join as join3, resolve } from "node:path";
19883
19904
  var REMOTE_PATTERNS = [
19884
19905
  /^git@[^:]+:(?<owner>[^/]+)\/(?<name>[^/]+?)(?:\.git)?$/,
19885
19906
  /^(?:ssh|git|https?):\/\/(?:[^@/]+@)?[^/]+\/(?<owner>[^/]+)\/(?<name>[^/]+?)(?:\.git)?\/?$/
@@ -20174,8 +20195,8 @@ function launchInTerminal(input2) {
20174
20195
  }
20175
20196
 
20176
20197
  // packages/core/src/worktree.ts
20177
- import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
20178
- import { join as join6, resolve, sep } from "node:path";
20198
+ import { existsSync as existsSync3, readFileSync as readFileSync3, realpathSync, writeFileSync as writeFileSync2 } from "node:fs";
20199
+ import { join as join6, resolve as resolve2, sep } from "node:path";
20179
20200
  async function defaultBranch(input2) {
20180
20201
  if (input2.configured) return input2.configured;
20181
20202
  try {
@@ -20199,12 +20220,44 @@ async function ensureWorktreesExcluded(input2) {
20199
20220
  writeFileSync2(path, `${existing}${suffix}.worktrees/
20200
20221
  `);
20201
20222
  }
20223
+ async function readWorktreeEntries(input2) {
20224
+ const { stdout } = await input2.runner("git", ["worktree", "list", "--porcelain"], {
20225
+ cwd: input2.repoRoot
20226
+ });
20227
+ const entries = [];
20228
+ let current = null;
20229
+ for (const line of stdout.split("\n")) {
20230
+ if (line.startsWith("worktree ")) {
20231
+ if (current) entries.push(current);
20232
+ current = {
20233
+ directory: line.slice("worktree ".length).trim(),
20234
+ branch: null,
20235
+ detached: false,
20236
+ bare: false
20237
+ };
20238
+ continue;
20239
+ }
20240
+ if (!current) continue;
20241
+ if (line.startsWith("branch ")) {
20242
+ current.branch = line.slice("branch ".length).replace(/^refs\/heads\//, "").trim();
20243
+ } else if (line === "detached") {
20244
+ current.detached = true;
20245
+ } else if (line === "bare") {
20246
+ current.bare = true;
20247
+ }
20248
+ }
20249
+ if (current) entries.push(current);
20250
+ return entries;
20251
+ }
20202
20252
  async function registeredWorktrees(input2) {
20203
- const { stdout } = await input2.runner("git", ["worktree", "list", "--porcelain"], { cwd: input2.repoRoot });
20204
- return stdout.split("\n").filter((line) => line.startsWith("worktree ")).map((line) => line.slice("worktree ".length).trim());
20253
+ return (await readWorktreeEntries(input2)).map((entry) => entry.directory);
20205
20254
  }
20206
20255
  function normalized(path) {
20207
- return resolve(path).replace(/\/+$/, "");
20256
+ try {
20257
+ return realpathSync(path).replace(/\/+$/, "");
20258
+ } catch {
20259
+ return resolve2(path).replace(/\/+$/, "");
20260
+ }
20208
20261
  }
20209
20262
  async function findOrphanWorktrees(input2) {
20210
20263
  const root = normalized(join6(input2.repoRoot, ".worktrees"));
@@ -20372,8 +20425,8 @@ function dispatchDialogue(input2) {
20372
20425
  }
20373
20426
 
20374
20427
  // packages/core/src/issue.ts
20375
- import { mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "node:fs";
20376
- import { dirname, join as join7 } from "node:path";
20428
+ import { mkdirSync as mkdirSync2, readFileSync as readFileSync4, renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "node:fs";
20429
+ import { dirname as dirname2, join as join7 } from "node:path";
20377
20430
  var SUMMARY_HEADING = /summary|要約|概要/i;
20378
20431
  var CRITERIA_HEADING = /acceptance criteria|受け入れ基準|完了条件|acceptance/i;
20379
20432
  var BULLET = /^\s*(?:[-*+]|\d+\.)\s+(?:\[[ xX]\]\s*)?(.+)$/;
@@ -20433,39 +20486,100 @@ async function fetchIssues(input2) {
20433
20486
  function issueCachePath(paths, repo) {
20434
20487
  return join7(paths.issueCacheDir, `${repo.owner}__${repo.name}.json`);
20435
20488
  }
20489
+ function normalizeCachedIssue(value) {
20490
+ if (!value || typeof value !== "object") return null;
20491
+ const issue2 = value;
20492
+ if (typeof issue2.number !== "number" || typeof issue2.title !== "string") return null;
20493
+ return {
20494
+ number: issue2.number,
20495
+ title: issue2.title,
20496
+ labels: Array.isArray(issue2.labels) ? issue2.labels.filter((label) => typeof label === "string") : [],
20497
+ state: typeof issue2.state === "string" ? issue2.state : "OPEN",
20498
+ updatedAt: typeof issue2.updatedAt === "string" ? issue2.updatedAt : (/* @__PURE__ */ new Date(0)).toISOString(),
20499
+ summary: typeof issue2.summary === "string" ? issue2.summary : "",
20500
+ criteria: Array.isArray(issue2.criteria) ? issue2.criteria.filter((criterion) => typeof criterion === "string") : []
20501
+ };
20502
+ }
20436
20503
  function readIssueCache(path) {
20437
20504
  try {
20438
20505
  const parsed = JSON.parse(readFileSync4(path, "utf8"));
20439
- return Array.isArray(parsed.issues) ? parsed : null;
20506
+ if (!Array.isArray(parsed.issues) || typeof parsed.fetchedAt !== "number") {
20507
+ rmSync2(path, { force: true });
20508
+ return null;
20509
+ }
20510
+ const issues = parsed.issues.map(normalizeCachedIssue);
20511
+ if (issues.some((issue2) => issue2 === null)) {
20512
+ rmSync2(path, { force: true });
20513
+ return null;
20514
+ }
20515
+ return { fetchedAt: parsed.fetchedAt, issues };
20440
20516
  } catch {
20517
+ try {
20518
+ rmSync2(path, { force: true });
20519
+ } catch {
20520
+ }
20441
20521
  return null;
20442
20522
  }
20443
20523
  }
20444
20524
  function writeIssueCache(path, cache) {
20445
- mkdirSync2(dirname(path), { recursive: true });
20446
- writeFileSync3(path, JSON.stringify(cache, null, 2));
20525
+ mkdirSync2(dirname2(path), { recursive: true });
20526
+ const temporary = `${path}.${process.pid}.${Date.now()}.tmp`;
20527
+ writeFileSync3(temporary, `${JSON.stringify(cache, null, 2)}
20528
+ `);
20529
+ renameSync2(temporary, path);
20447
20530
  }
20448
20531
  var CACHE_TTL_MS = 6e4;
20449
- async function loadIssues(input2) {
20532
+ function classifyGhError(error62) {
20533
+ const message = error62 instanceof Error ? error62.message : String(error62);
20534
+ if (/not authenticated|authentication|401|gh auth login/i.test(message)) {
20535
+ return { code: "auth", message };
20536
+ }
20537
+ if (/rate limit|secondary rate|403/i.test(message)) {
20538
+ return { code: "rate_limit", message };
20539
+ }
20540
+ if (/ENOTFOUND|ECONNREFUSED|ETIMEDOUT|fetch failed|network/i.test(message)) {
20541
+ return { code: "network", message };
20542
+ }
20543
+ if (/^gh\b|gh issue/i.test(message)) return { code: "gh", message };
20544
+ return { code: "unknown", message };
20545
+ }
20546
+ async function loadIssuesResult(input2) {
20450
20547
  const path = issueCachePath(input2.paths, input2.repo);
20451
20548
  const now = input2.now ?? Date.now();
20452
- if (!input2.force) {
20453
- const cache = readIssueCache(path);
20454
- if (cache && now - cache.fetchedAt < CACHE_TTL_MS) return cache.issues;
20549
+ const cache = readIssueCache(path);
20550
+ if (!input2.force && cache && now - cache.fetchedAt < CACHE_TTL_MS) {
20551
+ return { issues: cache.issues, cached: true, stale: false, fetchedAt: cache.fetchedAt, error: null };
20455
20552
  }
20456
- const issues = await fetchIssues({
20457
- repo: input2.repo,
20458
- config: input2.config,
20459
- runner: input2.runner,
20460
- cwd: input2.cwd
20461
- });
20462
- writeIssueCache(path, { fetchedAt: now, issues });
20463
- return issues;
20553
+ try {
20554
+ const issues = await fetchIssues({
20555
+ repo: input2.repo,
20556
+ config: input2.config,
20557
+ runner: input2.runner,
20558
+ cwd: input2.cwd
20559
+ });
20560
+ writeIssueCache(path, { fetchedAt: now, issues });
20561
+ return { issues, cached: false, stale: false, fetchedAt: now, error: null };
20562
+ } catch (error62) {
20563
+ const failure2 = classifyGhError(error62);
20564
+ if (cache) {
20565
+ return {
20566
+ issues: cache.issues,
20567
+ cached: true,
20568
+ stale: true,
20569
+ fetchedAt: cache.fetchedAt,
20570
+ error: failure2
20571
+ };
20572
+ }
20573
+ return { issues: [], cached: false, stale: false, fetchedAt: null, error: failure2 };
20574
+ }
20575
+ }
20576
+ async function loadIssues(input2) {
20577
+ return (await loadIssuesResult(input2)).issues;
20464
20578
  }
20465
20579
 
20466
20580
  // packages/core/src/skills.ts
20467
20581
  import { createHash } from "node:crypto";
20468
- import { cpSync, existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync2, readFileSync as readFileSync5, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "node:fs";
20582
+ import { cpSync, existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync2, readFileSync as readFileSync5, rmSync as rmSync3, writeFileSync as writeFileSync4 } from "node:fs";
20469
20583
  import { join as join8, relative, sep as sep2 } from "node:path";
20470
20584
  var MANIFEST_NAME = ".skills-deck.json";
20471
20585
  function hashSkillDir(dir) {
@@ -20552,7 +20666,7 @@ function installBundledSkills(input2) {
20552
20666
  result.unchanged.push(name);
20553
20667
  continue;
20554
20668
  }
20555
- rmSync2(target, { recursive: true, force: true });
20669
+ rmSync3(target, { recursive: true, force: true });
20556
20670
  cpSync(source, target, { recursive: true });
20557
20671
  manifest.skills[name] = { hash: sourceHash, updatedAt: now };
20558
20672
  result.updated.push(name);
@@ -21395,7 +21509,7 @@ function ControlTower() {
21395
21509
  async function withDialog(open2) {
21396
21510
  setDialogOpen(true);
21397
21511
  try {
21398
- await new Promise((resolve2) => setTimeout(resolve2, 0));
21512
+ await new Promise((resolve3) => setTimeout(resolve3, 0));
21399
21513
  return await open2();
21400
21514
  } finally {
21401
21515
  setDialogOpen(false);