@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
|
|
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": "
|
|
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##
|
|
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/
|
|
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
|
|
29338
|
-
if (
|
|
29339
|
-
|
|
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
|
-
|
|
29348
|
-
|
|
29349
|
-
|
|
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
|
+
"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",
|