@jaggerxtrm/specialists 3.6.3 → 3.6.5

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.
@@ -3,7 +3,7 @@
3
3
  "metadata": {
4
4
  "name": "researcher",
5
5
  "version": "1.1.0",
6
- "description": "Documentation and code researcher. Two modes: (1) targeted — look up current docs for a library, API, or framework relevant to the active job; (2) discovery — use ghgrep to find code patterns across GitHub, identify interesting repos, then deep-dive with deepwiki. Uses ctx7, deepwiki, and ghgrep CLIs. Keep-alive by default.",
6
+ "description": "Documentation, code, and media researcher. Three modes: (1) targeted — look up current docs for a library, API, or framework; (2) discovery — use ghgrep to find code patterns across GitHub, then deep-dive with deepwiki; (3) media — extract and analyze YouTube transcripts and social media content via last30days pipeline. Uses ctx7, deepwiki, ghgrep, and yt-dlp/last30days. Keep-alive by default.",
7
7
  "category": "analysis",
8
8
  "tags": [
9
9
  "docs",
@@ -23,20 +23,20 @@
23
23
  "stall_timeout_ms": 120000,
24
24
  "response_format": "markdown",
25
25
  "output_type": "research",
26
- "permission_required": "LOW",
26
+ "permission_required": "MEDIUM",
27
27
  "interactive": true,
28
28
  "max_retries": 0
29
29
  },
30
30
  "prompt": {
31
- "system": "You are a documentation and code researcher with two operating modes.\n\n## Mode 1: Targeted Lookup\n\nAnswer specific questions about libraries, APIs, or frameworks relevant to the current job.\nUse ctx7 for library/framework documentation. Use deepwiki for repo-specific internals.\n\nWhen to use: the bead or prompt asks about how a specific library works, what an API returns,\nwhat flags a CLI supports, or how a framework handles a specific pattern.\n\n## Mode 2: Discovery\n\nExplore what the wider ecosystem has built. Use ghgrep to search GitHub for code patterns\nand real-world implementations. When you find an interesting repository, use deepwiki to\ndeep-dive into its architecture, patterns, and conventions. Synthesize findings into\nactionable insights the team can apply.\n\nWhen to use: the bead or prompt asks \"how do others implement X?\", \"what's a good example\nof Y in the wild?\", or \"find repos that do Z well.\"\n\n## Tools Available\n\n### ghgrep — GitHub code search CLI\n\n```bash\nghgrep <query> [options]\n\n--lang <langs> comma-separated: TypeScript,TSX,Python,Go\n--repo <repo> filter by repo: facebook/react\n--path <path> file path pattern: \"packages/**\"\n--regexp regex mode (auto-prefixes (?s) for multiline)\n--case case-sensitive\n--words whole-word match\n--limit <n> max results (default: 10)\n--json raw JSON output\n```\n\nExamples:\n```bash\nghgrep \"useEffect(\" --lang TSX,TypeScript --limit 5\nghgrep \"AbortController\" --repo vercel/next.js --path \"packages/**\"\nghgrep \"class NotFoundError\" --regexp --lang TypeScript\n```\n\n### ctx7 — Context7 library documentation\n\nTwo-step process:\n```bash\n# Step 1: Resolve library ID\nnpx ctx7@latest library <name> \"<query>\"\n\n# Step 2: Fetch docs\nnpx ctx7@latest docs <libraryId> \"<query>\"\n```\n\n### deepwiki — GitHub repo documentation\n\n```bash\n# Table of contents for a repo\nnpx @seflless/deepwiki toc <owner/repo> --no-color -q\n\n# Ask a specific question about a repo\nnpx @seflless/deepwiki ask <owner/repo> \"<question>\" --no-color -q\n```\n\n## Discovery Workflow (Mode 2)\n\n1. Use ghgrep to search for code patterns relevant to the question\n2. Scan results to identify the most interesting/relevant repositories\n3. Use `deepwiki toc` to understand the selected repo's structure\n4. Use `deepwiki ask` to extract the specific pattern or design decision\n5. Synthesize findings into a structured report with concrete takeaways\n\n## Targeted Lookup Workflow (Mode 1)\n\n1. For library/framework questions → ctx7: resolve library ID, then fetch docs with query\n2. For GitHub repo internals (e.g. \"how does Vite handle X?\") → deepwiki ask\n3. Always run the actual CLI commands — do not answer from training knowledge\n4. Prefer targeted queries over broad ones; 1-3 CLI calls per sub-question\n\n## Constraints\n\n- READ and BASH onlydo not write or edit any files\n- Do not include API keys, credentials, or sensitive data in queries\n- If quota errors or CLI failures occur, report them explicitly — do not silently fall back\n to training data\n- This is a keep-alive specialist — after completing a research turn, enter waiting state\n ready for follow-up questions or new research directions\n",
32
- "task_template": "Research the following and return current documentation or findings with examples:\n\n$prompt\n\nChoose the appropriate mode:\n- **Targeted**: Use ctx7 or deepwiki to retrieve current docs for a specific library/API\n- **Discovery**: Use ghgrep to find real-world code patterns, identify interesting repos,\n then use deepwiki to deep-dive into the best ones\n\nSynthesize results into a clear, structured answer with code examples and actionable insights.\nAfter delivering your findings, enter keep-alive waiting state for follow-up questions.\n"
31
+ "system": "You are a documentation and code researcher with two operating modes.\n\n## Mode 1: Targeted Lookup\n\nAnswer specific questions about libraries, APIs, or frameworks relevant to the current job.\nUse ctx7 for library/framework documentation. Use deepwiki for repo-specific internals.\n\nWhen to use: the bead or prompt asks about how a specific library works, what an API returns,\nwhat flags a CLI supports, or how a framework handles a specific pattern.\n\n## Mode 2: Discovery\n\nExplore what the wider ecosystem has built. Use ghgrep to search GitHub for code patterns\nand real-world implementations. When you find an interesting repository, use deepwiki to\ndeep-dive into its architecture, patterns, and conventions. Synthesize findings into\nactionable insights the team can apply.\n\nWhen to use: the bead or prompt asks \"how do others implement X?\", \"what's a good example\nof Y in the wild?\", or \"find repos that do Z well.\"\n\n## Tools Available\n\n### ghgrep — GitHub code search CLI\n\n```bash\nghgrep <query> [options]\n\n--lang <langs> comma-separated: TypeScript,TSX,Python,Go\n--repo <repo> filter by repo: facebook/react\n--path <path> file path pattern: \"packages/**\"\n--regexp regex mode (auto-prefixes (?s) for multiline)\n--case case-sensitive\n--words whole-word match\n--limit <n> max results (default: 10)\n--json raw JSON output\n```\n\nExamples:\n```bash\nghgrep \"useEffect(\" --lang TSX,TypeScript --limit 5\nghgrep \"AbortController\" --repo vercel/next.js --path \"packages/**\"\nghgrep \"class NotFoundError\" --regexp --lang TypeScript\n```\n\n### ctx7 — Context7 library documentation\n\nTwo-step process:\n```bash\n# Step 1: Resolve library ID\nnpx ctx7@latest library <name> \"<query>\"\n\n# Step 2: Fetch docs\nnpx ctx7@latest docs <libraryId> \"<query>\"\n```\n\n### deepwiki — GitHub repo documentation\n\n```bash\n# Table of contents for a repo\nnpx @seflless/deepwiki toc <owner/repo> --no-color -q\n\n# Ask a specific question about a repo\nnpx @seflless/deepwiki ask <owner/repo> \"<question>\" --no-color -q\n```\n\n## Discovery Workflow (Mode 2)\n\n1. Use ghgrep to search for code patterns relevant to the question\n2. Scan results to identify the most interesting/relevant repositories\n3. Use `deepwiki toc` to understand the selected repo's structure\n4. Use `deepwiki ask` to extract the specific pattern or design decision\n5. Synthesize findings into a structured report with concrete takeaways\n\n## Targeted Lookup Workflow (Mode 1)\n\n1. For library/framework questions → ctx7: resolve library ID, then fetch docs with query\n2. For GitHub repo internals (e.g. \"how does Vite handle X?\") → deepwiki ask\n3. Always run the actual CLI commands — do not answer from training knowledge\n4. Prefer targeted queries over broad ones; 1-3 CLI calls per sub-question\n\n## Mode 3: Media Research (YouTube transcripts, social media)\n\nExtract and analyze content from YouTube videos and social media platforms.\nUse the last30days pipeline for multi-source research, or yt-dlp directly for\nsingle-video transcript extraction.\n\nWhen to use: the prompt references a YouTube URL, asks to analyze video content,\nor requests social media research on a topic.\n\n### Single video transcript extraction\n\n```bash\n# Find the skill root\nfor dir in \\\n \".\" \\\n \"${CLAUDE_PLUGIN_ROOT:-}\" \\\n \"$HOME/.claude/skills/last30days\" \\\n \"$HOME/.agents/skills/last30days\"; do\n [ -n \"$dir\" ] && [ -f \"$dir/scripts/last30days.py\" ] && SKILL_ROOT=\"$dir\" && break\ndone\n\n# Extract transcript from a single video\npython3 -c \"\nimport sys; sys.path.insert(0, '${SKILL_ROOT}/scripts')\nfrom lib.youtube_yt import fetch_transcript, extract_transcript_highlights, _clean_vtt\nimport tempfile\nwith tempfile.TemporaryDirectory() as td:\n transcript = fetch_transcript('VIDEO_ID', td)\nif transcript:\n print(transcript[:10000])\n highlights = extract_transcript_highlights(transcript, 'TOPIC', limit=10)\n print('\\n--- Highlights ---')\n for h in highlights: print(f'- {h}')\nelse:\n print('No transcript available')\n\"\n```\n\nReplace VIDEO_ID with the YouTube video ID (the part after v= or the last path segment).\nReplace TOPIC with relevant keywords for highlight extraction.\n\n### Multi-source topic research\n\n```bash\npython3 \"${SKILL_ROOT}/scripts/last30days.py\" TOPIC --emit=compact --no-native-web --save-dir=~/Documents/Last30Days\n```\n\n### Key notes for Mode 3\n- Non-English videos ARE supported transcripts are fetched in the original language\n- Transcript highlights use keyword scoring — provide topic words in the video's language\n- For long videos (>5000 words), summarize key sections rather than dumping the full transcript\n- Always report: language detected, word count, number of highlights extracted\n\n## Constraints\n\n- Do not write or edit project source files\n- Do not include API keys, credentials, or sensitive data in queries\n- If quota errors or CLI failures occur, report them explicitly — do not silently fall back\n to training data\n- This is a keep-alive specialist — after completing a research turn, enter waiting state\n ready for follow-up questions or new research directions\n",
32
+ "task_template": "Research the following and return current documentation or findings with examples:\n\n$prompt\n\nChoose the appropriate mode:\n- **Targeted**: Use ctx7 or deepwiki to retrieve current docs for a specific library/API\n- **Discovery**: Use ghgrep to find real-world code patterns, identify interesting repos,\n then use deepwiki to deep-dive into the best ones\n- **Media**: Use yt-dlp/last30days to extract YouTube transcripts or research social media content\n\nSynthesize results into a clear, structured answer with code examples and actionable insights.\nAfter delivering your findings, enter keep-alive waiting state for follow-up questions.\n"
33
33
  },
34
34
  "skills": {
35
35
  "paths": [
36
36
  ".xtrm/skills/active/pi/find-docs/SKILL.md",
37
37
  ".xtrm/skills/active/pi/deepwiki/SKILL.md",
38
38
  ".xtrm/skills/active/pi/github-search/SKILL.md",
39
- ".xtrm/skills/default/last30days"
39
+ ".xtrm/skills/active/pi/last30days/SKILL.md"
40
40
  ],
41
41
  "scripts": []
42
42
  },
package/dist/index.js CHANGED
@@ -29070,6 +29070,27 @@ function runCommand(command, args, cwd = process.cwd()) {
29070
29070
  stdio: ["ignore", "pipe", "pipe"]
29071
29071
  });
29072
29072
  }
29073
+ function resolveMainWorktreeRoot(cwd = process.cwd()) {
29074
+ const worktreeList = runCommand("git", ["worktree", "list", "--porcelain"], cwd);
29075
+ if (worktreeList.status === 0) {
29076
+ const firstWorktreeLine = worktreeList.stdout.split(`
29077
+ `).map((line) => line.trim()).find((line) => line.startsWith("worktree "));
29078
+ if (firstWorktreeLine) {
29079
+ const worktreePath = firstWorktreeLine.slice("worktree ".length).trim();
29080
+ if (worktreePath)
29081
+ return worktreePath;
29082
+ }
29083
+ }
29084
+ const topLevel = runCommand("git", ["rev-parse", "--show-toplevel"], cwd);
29085
+ if (topLevel.status !== 0) {
29086
+ throw new Error("Unable to resolve main worktree root.");
29087
+ }
29088
+ const rootPath = topLevel.stdout.trim();
29089
+ if (!rootPath) {
29090
+ throw new Error("Unable to resolve main worktree root.");
29091
+ }
29092
+ return rootPath;
29093
+ }
29073
29094
  function readJson(text) {
29074
29095
  try {
29075
29096
  return JSON.parse(text);
@@ -29308,8 +29329,8 @@ function resolveMergeTargets(target) {
29308
29329
  }
29309
29330
  return chains;
29310
29331
  }
29311
- function readChangedFilesForLastMerge() {
29312
- const diff = runCommand("git", ["diff", "--name-only", "HEAD^1", "HEAD"]);
29332
+ function readChangedFilesForLastMerge(cwd = process.cwd()) {
29333
+ const diff = runCommand("git", ["diff", "--name-only", "HEAD^1", "HEAD"], cwd);
29313
29334
  if (diff.status !== 0)
29314
29335
  return [];
29315
29336
  return diff.stdout.split(`
@@ -29333,35 +29354,29 @@ function parseNameStatusLine(line) {
29333
29354
  function isNoisePath(path) {
29334
29355
  return NOISE_PATH_PREFIXES.some((prefix) => path.startsWith(prefix));
29335
29356
  }
29336
- function previewBranchMergeDelta(branch) {
29337
- const previewMerge = runCommand("git", ["merge", branch, "--no-ff", "--no-commit"]);
29338
- if (previewMerge.status !== 0) {
29339
- const conflicts = getConflictFiles();
29340
- runCommand("git", ["merge", "--abort"]);
29341
- const conflictContext = conflicts.length > 0 ? `
29342
- Conflicting files:
29343
- ${conflicts.map((file) => `- ${file}`).join(`
29344
- `)}` : "";
29345
- throw new Error(`Unable to preview merge for '${branch}'.${conflictContext}`);
29357
+ function previewBranchMergeDelta(branch, cwd = process.cwd()) {
29358
+ const mergeBase = runCommand("git", ["merge-base", "main", branch], cwd);
29359
+ if (mergeBase.status !== 0) {
29360
+ throw new Error(`Unable to compute merge base for 'main' and '${branch}'.`);
29346
29361
  }
29347
- try {
29348
- const stagedDelta = runCommand("git", ["diff", "--cached", "--name-status"]);
29349
- if (stagedDelta.status !== 0) {
29350
- throw new Error(`Unable to read staged merge delta for '${branch}'.`);
29351
- }
29352
- const files = stagedDelta.stdout.split(`
29353
- `).map(parseNameStatusLine).filter((entry) => Boolean(entry));
29354
- const noiseFiles = files.filter((file) => isNoisePath(file.path));
29355
- const substantiveFiles = files.filter((file) => !isNoisePath(file.path));
29356
- return {
29357
- branch,
29358
- files,
29359
- noiseFiles,
29360
- substantiveFiles
29361
- };
29362
- } finally {
29363
- runCommand("git", ["merge", "--abort"]);
29362
+ const mergeBaseSha = mergeBase.stdout.trim();
29363
+ if (!mergeBaseSha) {
29364
+ throw new Error(`Unable to compute merge base for 'main' and '${branch}'.`);
29364
29365
  }
29366
+ const stagedDelta = runCommand("git", ["diff", `${mergeBaseSha}..${branch}`, "--name-status"], cwd);
29367
+ if (stagedDelta.status !== 0) {
29368
+ throw new Error(`Unable to read merge delta for '${branch}'.`);
29369
+ }
29370
+ const files = stagedDelta.stdout.split(`
29371
+ `).map(parseNameStatusLine).filter((entry) => Boolean(entry));
29372
+ const noiseFiles = files.filter((file) => isNoisePath(file.path));
29373
+ const substantiveFiles = files.filter((file) => !isNoisePath(file.path));
29374
+ return {
29375
+ branch,
29376
+ files,
29377
+ noiseFiles,
29378
+ substantiveFiles
29379
+ };
29365
29380
  }
29366
29381
  function evaluateMergeWorthiness(preview) {
29367
29382
  if (preview.files.length === 0) {
@@ -29385,33 +29400,33 @@ function throwWorthinessBlockError(target, preview, decision) {
29385
29400
  throw new Error(`Refusing merge for '${target.branch}': ${reason}.
29386
29401
  ` + `Diagnostics: ${summary}`);
29387
29402
  }
29388
- function assertBranchMergeWorthiness(target) {
29389
- const preview = previewBranchMergeDelta(target.branch);
29403
+ function assertBranchMergeWorthiness(target, cwd = process.cwd()) {
29404
+ const preview = previewBranchMergeDelta(target.branch, cwd);
29390
29405
  const decision = evaluateMergeWorthiness(preview);
29391
29406
  if (decision.shouldMerge)
29392
29407
  return;
29393
29408
  throwWorthinessBlockError(target, preview, decision);
29394
29409
  }
29395
- function getConflictFiles() {
29396
- const result = runCommand("git", ["diff", "--name-only", "--diff-filter=U"]);
29410
+ function getConflictFiles(cwd = process.cwd()) {
29411
+ const result = runCommand("git", ["diff", "--name-only", "--diff-filter=U"], cwd);
29397
29412
  if (result.status !== 0)
29398
29413
  return [];
29399
29414
  return result.stdout.split(`
29400
29415
  `).map((line) => line.trim()).filter(Boolean);
29401
29416
  }
29402
- function mergeBranch(branch) {
29403
- const result = runCommand("git", ["merge", branch, "--no-ff", "--no-edit"]);
29417
+ function mergeBranch(branch, cwd = process.cwd()) {
29418
+ const result = runCommand("git", ["merge", branch, "--no-ff", "--no-edit"], cwd);
29404
29419
  if (result.status === 0)
29405
29420
  return;
29406
- const conflicts = getConflictFiles();
29421
+ const conflicts = getConflictFiles(cwd);
29407
29422
  const context = conflicts.length > 0 ? `
29408
29423
  Conflicting files:
29409
29424
  ${conflicts.map((file) => `- ${file}`).join(`
29410
29425
  `)}` : "";
29411
29426
  throw new Error(`Merge conflict while merging '${branch}'.${context}`);
29412
29427
  }
29413
- function runTypecheckGate() {
29414
- const tsc = runCommand("bunx", ["tsc", "--noEmit"]);
29428
+ function runTypecheckGate(cwd = process.cwd()) {
29429
+ const tsc = runCommand("bunx", ["tsc", "--noEmit"], cwd);
29415
29430
  if (tsc.status === 0)
29416
29431
  return;
29417
29432
  const stderr = tsc.stderr.trim();
@@ -29419,8 +29434,8 @@ function runTypecheckGate() {
29419
29434
  throw new Error(`TypeScript gate failed after merge.
29420
29435
  ${stderr || stdout || "Unknown tsc error"}`);
29421
29436
  }
29422
- function runRebuild() {
29423
- const build = runCommand("bun", ["run", "build"]);
29437
+ function runRebuild(cwd = process.cwd()) {
29438
+ const build = runCommand("bun", ["run", "build"], cwd);
29424
29439
  if (build.status === 0)
29425
29440
  return;
29426
29441
  const stderr = build.stderr.trim();
@@ -29450,19 +29465,20 @@ function printUsageAndExit(message) {
29450
29465
  process.exit(1);
29451
29466
  }
29452
29467
  function runMergePlan(targets, options) {
29468
+ const mainRepoRoot = resolveMainWorktreeRoot();
29453
29469
  const mergedSteps = [];
29454
29470
  for (const target of targets) {
29455
- assertBranchMergeWorthiness(target);
29456
- mergeBranch(target.branch);
29457
- runTypecheckGate();
29471
+ assertBranchMergeWorthiness(target, mainRepoRoot);
29472
+ mergeBranch(target.branch, mainRepoRoot);
29473
+ runTypecheckGate(mainRepoRoot);
29458
29474
  mergedSteps.push({
29459
29475
  beadId: target.beadId,
29460
29476
  branch: target.branch,
29461
- changedFiles: readChangedFilesForLastMerge()
29477
+ changedFiles: readChangedFilesForLastMerge(mainRepoRoot)
29462
29478
  });
29463
29479
  }
29464
29480
  if (options.rebuild) {
29465
- runRebuild();
29481
+ runRebuild(mainRepoRoot);
29466
29482
  }
29467
29483
  return mergedSteps;
29468
29484
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jaggerxtrm/specialists",
3
- "version": "3.6.3",
3
+ "version": "3.6.5",
4
4
  "description": "OmniSpecialist — 7-tool MCP orchestration layer powered by the Specialist System. Discover and execute .specialist.yaml files across project/user/system scopes via pi.",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",