@nahisaho/satori 0.26.0 → 0.27.1
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/bin/satori.js +430 -4
- package/package.json +1 -1
package/bin/satori.js
CHANGED
|
@@ -71,6 +71,8 @@ SATORI — Agent Skills for Science
|
|
|
71
71
|
|
|
72
72
|
Usage:
|
|
73
73
|
satori init [--force] [--dry-run] Install .github/ skills into current directory
|
|
74
|
+
satori skill search <query> Search skills by keyword
|
|
75
|
+
satori skill info <name> Show detailed skill information
|
|
74
76
|
satori pipeline suggest Interactive pipeline recommendation
|
|
75
77
|
satori pipeline list List all available pipelines
|
|
76
78
|
satori validate [--verbose] Validate all SKILL.md files
|
|
@@ -275,6 +277,200 @@ const PIPELINES = [
|
|
|
275
277
|
keywords: ['教育', 'education', 'カリキュラム'],
|
|
276
278
|
skills: 'science-education → reproducibility-assessment',
|
|
277
279
|
},
|
|
280
|
+
// ── クロスドメインパイプライン ──
|
|
281
|
+
{
|
|
282
|
+
id: 'A',
|
|
283
|
+
name: 'ゲノム創薬統合',
|
|
284
|
+
domain: 'cross-domain',
|
|
285
|
+
keywords: ['ゲノム創薬', 'GWAS', '創薬ターゲット', 'drug target', 'biobank'],
|
|
286
|
+
skills:
|
|
287
|
+
'biobank-cohort → population-genetics → drug-target-profiling → compound-screening → molecular-docking → admet-pharmacokinetics',
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
id: 'B',
|
|
291
|
+
name: 'AI 駆動臨床意思決定',
|
|
292
|
+
domain: 'cross-domain',
|
|
293
|
+
keywords: ['臨床AI', '予後予測', 'SHAP', '患者', 'clinical AI'],
|
|
294
|
+
skills: 'clinical-decision-support → healthcare-ai → explainable-ai → pharmacovigilance → regulatory-science',
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
id: 'C',
|
|
298
|
+
name: '研究自動化',
|
|
299
|
+
domain: 'cross-domain',
|
|
300
|
+
keywords: ['研究自動化', '論文化', '仮説', 'research automation'],
|
|
301
|
+
skills:
|
|
302
|
+
'deep-research → hypothesis-pipeline → pipeline-scaffold → data-preprocessing → statistical-testing → publication-figures → academic-writing → systematic-review',
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
id: 'D',
|
|
306
|
+
name: 'マルチオミクス疾患解明',
|
|
307
|
+
domain: 'cross-domain',
|
|
308
|
+
keywords: ['マルチオミクス', '疾患', 'scRNA-seq', 'GRN', 'multi-omics'],
|
|
309
|
+
skills:
|
|
310
|
+
'single-cell-genomics → spatial-transcriptomics → disease-research → systems-biology → multi-omics → network-analysis',
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
id: 'E',
|
|
314
|
+
name: '個別化薬物療法',
|
|
315
|
+
domain: 'cross-domain',
|
|
316
|
+
keywords: ['個別化医療', 'PGx', 'Star アレル', '投与量最適化', 'pharmacogenomics'],
|
|
317
|
+
skills:
|
|
318
|
+
'variant-interpretation → pharmacogenomics → drug-target-profiling → admet-pharmacokinetics → clinical-decision-support → pharmacovigilance',
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
id: 'F',
|
|
322
|
+
name: 'バイオインフォマティクス完全',
|
|
323
|
+
domain: 'cross-domain',
|
|
324
|
+
keywords: ['バイオインフォマティクス', 'FASTQ', '配列解析', '統合パイプライン'],
|
|
325
|
+
skills:
|
|
326
|
+
'bioinformatics → single-cell-genomics → biobank-cohort → multi-omics → population-genetics → systems-biology → hypothesis-pipeline → academic-writing',
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
id: 'G',
|
|
330
|
+
name: 'がん精密医療 End-to-End',
|
|
331
|
+
domain: 'cross-domain',
|
|
332
|
+
keywords: ['がん精密医療', 'GDC', 'DepMap', '精密腫瘍学', 'TCGA'],
|
|
333
|
+
skills:
|
|
334
|
+
'gdc-portal → cancer-genomics → depmap-dependencies → civic-evidence → pharos-targets → compound-screening → precision-oncology → clinical-decision-support → healthcare-ai → survival-clinical',
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
id: 'H',
|
|
338
|
+
name: 'マルチオミクス縦断統合',
|
|
339
|
+
domain: 'cross-domain',
|
|
340
|
+
keywords: ['縦断統合', 'エピゲノム', 'プロテオーム', 'パスウェイ', 'VEP'],
|
|
341
|
+
skills:
|
|
342
|
+
'genome-sequence-tools → bioinformatics → variant-effect-prediction → epigenomics-chromatin → regulatory-genomics → cellxgene-census → scvi-integration → uniprot-proteome → alphafold-structures → protein-interaction-network → pathway-enrichment → reactome-pathways → network-visualization',
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
id: 'I',
|
|
346
|
+
name: '環境メタボ・マイクロバイオーム One Health',
|
|
347
|
+
domain: 'cross-domain',
|
|
348
|
+
keywords: ['One Health', '環境メタボ', '土壌', '微生物群集', 'SDM'],
|
|
349
|
+
skills:
|
|
350
|
+
'environmental-ecology → environmental-geodata → geospatial-analysis → microbiome-metagenomics → metagenome-assembled-genomes → phylogenetics → metabolomics-databases → metabolomics-network → metabolic-modeling → toxicology-env → publication-figures',
|
|
351
|
+
},
|
|
352
|
+
{
|
|
353
|
+
id: 'J',
|
|
354
|
+
name: 'AI 駆動マテリアルズインフォマティクス',
|
|
355
|
+
domain: 'cross-domain',
|
|
356
|
+
keywords: ['マテリアルズインフォマティクス', 'GNN', '能動学習', 'Materials Project', '材料探索'],
|
|
357
|
+
skills:
|
|
358
|
+
'computational-materials → cheminformatics → automl → graph-neural-networks → uncertainty-quantification → active-learning → doe → bayesian-statistics → adaptive-experiments → materials-characterization → advanced-visualization',
|
|
359
|
+
},
|
|
360
|
+
{
|
|
361
|
+
id: 'K',
|
|
362
|
+
name: '研究ライフサイクル完全自動化',
|
|
363
|
+
domain: 'cross-domain',
|
|
364
|
+
keywords: ['研究ライフサイクル', 'ラボ自動化', 'LIMS', 'ダッシュボード', 'グラント'],
|
|
365
|
+
skills:
|
|
366
|
+
'lab-automation → lab-data-management → streaming-analytics → model-monitoring → data-profiling → advanced-visualization → interactive-dashboard → scientific-schematics → reproducible-reporting → paper-quality → latex-formatter → peer-review-response → grant-writing → preprint-archive',
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
id: 'L',
|
|
370
|
+
name: 'AI 駆動エビデンス合成',
|
|
371
|
+
domain: 'cross-domain',
|
|
372
|
+
keywords: ['エビデンス合成AI', 'DL文献', 'AutoML', 'スクリーニング'],
|
|
373
|
+
skills:
|
|
374
|
+
'deep-research → literature-search → text-mining-nlp → deep-learning → transfer-learning → automl → meta-analysis → explainable-ai → systematic-review → academic-writing',
|
|
375
|
+
},
|
|
376
|
+
{
|
|
377
|
+
id: 'M',
|
|
378
|
+
name: 'がんマルチレイヤーゲノム創薬',
|
|
379
|
+
domain: 'cross-domain',
|
|
380
|
+
keywords: ['がんゲノム創薬', 'ICGC', 'ChEMBL', 'エピゲノム'],
|
|
381
|
+
skills:
|
|
382
|
+
'gdc-portal → cancer-genomics → icgc-cancer-data → ensembl-genomics → variant-effect-prediction → epigenomics-chromatin → gwas-catalog → pharos-targets → chembl-assay-mining → compound-screening',
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
id: 'N',
|
|
386
|
+
name: '臨床→規制→出版バリューチェーン',
|
|
387
|
+
domain: 'cross-domain',
|
|
388
|
+
keywords: ['バリューチェーン', 'EHR', '規制報告', '学術出版', 'HL7'],
|
|
389
|
+
skills:
|
|
390
|
+
'clinical-standards → clinical-nlp → clinical-reporting → healthcare-ai → pharmacovigilance → regulatory-science → reproducible-reporting → paper-quality → latex-formatter → peer-review-response',
|
|
391
|
+
},
|
|
392
|
+
{
|
|
393
|
+
id: 'O',
|
|
394
|
+
name: 'シングルセルプロテオーム統合',
|
|
395
|
+
domain: 'cross-domain',
|
|
396
|
+
keywords: ['シングルセルプロテオーム', '質量分析', '代謝モデル', 'MOFA+'],
|
|
397
|
+
skills:
|
|
398
|
+
'single-cell-genomics → spatial-transcriptomics → proteomics-mass-spectrometry → structural-proteomics → alphafold-structures → metabolomics-databases → metabolic-modeling → systems-biology → multi-omics',
|
|
399
|
+
},
|
|
400
|
+
// ── インダストリーパイプライン ──
|
|
401
|
+
{
|
|
402
|
+
id: 'Ind-1',
|
|
403
|
+
name: '製薬企業レギュラトリー',
|
|
404
|
+
domain: 'industry',
|
|
405
|
+
keywords: ['製薬', 'CTD', 'レギュラトリー', '規制申請', 'regulatory'],
|
|
406
|
+
skills:
|
|
407
|
+
'drug-target-profiling → molecular-docking → admet-pharmacokinetics → clinical-trials-analytics → pharmacovigilance → regulatory-science → reproducible-reporting → paper-quality',
|
|
408
|
+
},
|
|
409
|
+
{
|
|
410
|
+
id: 'Ind-2',
|
|
411
|
+
name: '農業バイオテクノロジー',
|
|
412
|
+
domain: 'industry',
|
|
413
|
+
keywords: ['農業バイオ', '土壌微生物', 'CRISPR', '圃場', 'ゲノム編集'],
|
|
414
|
+
skills:
|
|
415
|
+
'environmental-ecology → microbiome-metagenomics → geospatial-analysis → plant-biology → crispr-design → gene-expression-transcriptomics → doe → publication-figures',
|
|
416
|
+
},
|
|
417
|
+
{
|
|
418
|
+
id: 'Ind-3',
|
|
419
|
+
name: '臨床検査室ワークフロー',
|
|
420
|
+
domain: 'industry',
|
|
421
|
+
keywords: ['臨床検査', 'NGS', 'ACMG', 'PGx', '臨床レポート'],
|
|
422
|
+
skills:
|
|
423
|
+
'genome-sequence-tools → variant-interpretation → pharmacogenomics → clinical-decision-support → clinical-standards → clinical-nlp → clinical-reporting',
|
|
424
|
+
},
|
|
425
|
+
{
|
|
426
|
+
id: 'Ind-4',
|
|
427
|
+
name: '食品安全・毒性評価',
|
|
428
|
+
domain: 'industry',
|
|
429
|
+
keywords: ['食品安全', '毒性', '残留農薬', 'フードセーフティ', 'food safety'],
|
|
430
|
+
skills:
|
|
431
|
+
'microbiome-metagenomics → rrna-taxonomy → metabolomics-databases → metabolomics-network → toxicology-env → data-profiling → regulatory-science → publication-figures',
|
|
432
|
+
},
|
|
433
|
+
{
|
|
434
|
+
id: 'Ind-5',
|
|
435
|
+
name: '法医・公衆衛生',
|
|
436
|
+
domain: 'industry',
|
|
437
|
+
keywords: ['法医学', '公衆衛生', 'アウトブレイク', 'サーベイランス', 'forensic'],
|
|
438
|
+
skills:
|
|
439
|
+
'variant-interpretation → population-genetics → infectious-disease → phylogenetics → immunoinformatics → epidemiology-public-health → public-health-data → biobank-cohort',
|
|
440
|
+
},
|
|
441
|
+
// ── メソドロジーパイプライン ──
|
|
442
|
+
{
|
|
443
|
+
id: 'M-α',
|
|
444
|
+
name: 'ベイズ推論ワークフロー',
|
|
445
|
+
domain: 'methodology',
|
|
446
|
+
keywords: ['ベイズ', 'MCMC', '事後分布', 'Bayesian', '事前分布'],
|
|
447
|
+
skills:
|
|
448
|
+
'data-preprocessing → bayesian-statistics → statistical-simulation → uncertainty-quantification → doe → adaptive-experiments',
|
|
449
|
+
},
|
|
450
|
+
{
|
|
451
|
+
id: 'M-β',
|
|
452
|
+
name: '因果推論パイプライン',
|
|
453
|
+
domain: 'methodology',
|
|
454
|
+
keywords: ['因果推論', 'DAG', '傾向スコア', 'CATE', 'causal'],
|
|
455
|
+
skills:
|
|
456
|
+
'data-preprocessing → missing-data-analysis → causal-inference → causal-ml → explainable-ai → statistical-testing → publication-figures',
|
|
457
|
+
},
|
|
458
|
+
{
|
|
459
|
+
id: 'M-γ',
|
|
460
|
+
name: '時系列予測パイプライン',
|
|
461
|
+
domain: 'methodology',
|
|
462
|
+
keywords: ['時系列', 'Prophet', 'ARIMA', 'LSTM', '異常検知', 'forecasting'],
|
|
463
|
+
skills:
|
|
464
|
+
'data-preprocessing → time-series → time-series-forecasting → anomaly-detection → streaming-analytics → model-monitoring',
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
id: 'M-δ',
|
|
468
|
+
name: 'テキストマイニング・NLP',
|
|
469
|
+
domain: 'methodology',
|
|
470
|
+
keywords: ['テキストマイニング', 'NLP', 'PubTator', '引用ネットワーク', 'NER'],
|
|
471
|
+
skills:
|
|
472
|
+
'deep-research → literature-search → text-mining-nlp → biomedical-pubtator → clinical-nlp → semantic-scholar → citation-checker',
|
|
473
|
+
},
|
|
278
474
|
];
|
|
279
475
|
|
|
280
476
|
function pipelineSuggest() {
|
|
@@ -326,6 +522,7 @@ function pipelineSuggest() {
|
|
|
326
522
|
console.log(` ... 他 ${scored.length - 5} 件`);
|
|
327
523
|
}
|
|
328
524
|
console.log('詳細は docs/SATORI_PIPELINE_EXAMPLES.md を参照してください。');
|
|
525
|
+
console.log('全パイプライン一覧は `satori pipeline list` で確認できます。');
|
|
329
526
|
}
|
|
330
527
|
|
|
331
528
|
rl.close();
|
|
@@ -333,14 +530,42 @@ function pipelineSuggest() {
|
|
|
333
530
|
}
|
|
334
531
|
|
|
335
532
|
function pipelineList() {
|
|
336
|
-
|
|
337
|
-
|
|
533
|
+
const domain = PIPELINES.filter((p) => typeof p.id === 'number');
|
|
534
|
+
const cross = PIPELINES.filter((p) => p.domain === 'cross-domain');
|
|
535
|
+
const industry = PIPELINES.filter((p) => p.domain === 'industry');
|
|
536
|
+
const methodology = PIPELINES.filter((p) => p.domain === 'methodology');
|
|
537
|
+
|
|
538
|
+
console.log(`\n📋 SATORI パイプライン一覧 (全 ${PIPELINES.length} パイプライン)\n`);
|
|
539
|
+
|
|
540
|
+
console.log('── ドメインパイプライン (26) ──\n');
|
|
541
|
+
for (const p of domain) {
|
|
338
542
|
console.log(` #${String(p.id).padStart(2, ' ')} ${p.name}`);
|
|
339
543
|
console.log(` ${p.skills}`);
|
|
340
544
|
console.log('');
|
|
341
545
|
}
|
|
342
|
-
|
|
343
|
-
console.log('
|
|
546
|
+
|
|
547
|
+
console.log('── クロスドメインパイプライン (15) ──\n');
|
|
548
|
+
for (const p of cross) {
|
|
549
|
+
console.log(` #${p.id} ${p.name}`);
|
|
550
|
+
console.log(` ${p.skills}`);
|
|
551
|
+
console.log('');
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
console.log('── インダストリーパイプライン (5) ──\n');
|
|
555
|
+
for (const p of industry) {
|
|
556
|
+
console.log(` #${p.id} ${p.name}`);
|
|
557
|
+
console.log(` ${p.skills}`);
|
|
558
|
+
console.log('');
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
console.log('── メソドロジーパイプライン (4) ──\n');
|
|
562
|
+
for (const p of methodology) {
|
|
563
|
+
console.log(` #${p.id} ${p.name}`);
|
|
564
|
+
console.log(` ${p.skills}`);
|
|
565
|
+
console.log('');
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
console.log('詳細は docs/SATORI_PIPELINE_EXAMPLES.md を参照してください。');
|
|
344
569
|
}
|
|
345
570
|
|
|
346
571
|
function showVersion() {
|
|
@@ -480,10 +705,211 @@ function stats() {
|
|
|
480
705
|
`);
|
|
481
706
|
}
|
|
482
707
|
|
|
708
|
+
// ── Skill Search / Info ──
|
|
709
|
+
|
|
710
|
+
function loadAllSkills() {
|
|
711
|
+
const skillsDir = path.join(SOURCE_DIR, 'skills');
|
|
712
|
+
if (!fs.existsSync(skillsDir)) {
|
|
713
|
+
console.error('Error: skills directory not found:', skillsDir);
|
|
714
|
+
process.exit(1);
|
|
715
|
+
}
|
|
716
|
+
const dirs = fs
|
|
717
|
+
.readdirSync(skillsDir)
|
|
718
|
+
.filter((d) => d.startsWith('scientific-'))
|
|
719
|
+
.sort();
|
|
720
|
+
const skills = [];
|
|
721
|
+
for (const dir of dirs) {
|
|
722
|
+
const filePath = path.join(skillsDir, dir, 'SKILL.md');
|
|
723
|
+
if (!fs.existsSync(filePath)) continue;
|
|
724
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
725
|
+
const fm = parseFrontmatter(content);
|
|
726
|
+
const descMatch = content.match(/^description:\s*\|?\s*\n([\s\S]*?)(?=\n\w|\n---)/m);
|
|
727
|
+
const description = descMatch ? descMatch[1].replace(/^\s+/gm, '').trim() : fm?.hasDescription ? '' : '';
|
|
728
|
+
const tuKeyPattern = /`([A-Z][a-zA-Z]*_[a-z]+_[a-z_]+)`/g;
|
|
729
|
+
const tuKeys = [];
|
|
730
|
+
for (const m of content.matchAll(tuKeyPattern)) {
|
|
731
|
+
tuKeys.push(m[1]);
|
|
732
|
+
}
|
|
733
|
+
const h1Match = content.match(/^# (.+)$/m);
|
|
734
|
+
const title = h1Match ? h1Match[1].trim() : dir;
|
|
735
|
+
skills.push({ dir, name: fm?.name || dir, title, description, content, tuKeys });
|
|
736
|
+
}
|
|
737
|
+
return skills;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
function skillSearch() {
|
|
741
|
+
const query = process.argv.slice(4).join(' ').toLowerCase();
|
|
742
|
+
if (!query) {
|
|
743
|
+
console.error('Error: 検索クエリを指定してください。');
|
|
744
|
+
console.log('Usage: satori skill search <query>');
|
|
745
|
+
process.exit(1);
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
const skills = loadAllSkills();
|
|
749
|
+
const scored = skills
|
|
750
|
+
.map((s) => {
|
|
751
|
+
let score = 0;
|
|
752
|
+
// 名前の完全一致
|
|
753
|
+
if (s.name.toLowerCase() === query) score += 10;
|
|
754
|
+
// 名前に含まれる
|
|
755
|
+
else if (s.name.toLowerCase().includes(query)) score += 5;
|
|
756
|
+
// タイトルに含まれる
|
|
757
|
+
if (s.title.toLowerCase().includes(query)) score += 3;
|
|
758
|
+
// 説明に含まれる
|
|
759
|
+
if (s.description.toLowerCase().includes(query)) score += 2;
|
|
760
|
+
// TU キーに含まれる
|
|
761
|
+
for (const k of s.tuKeys) {
|
|
762
|
+
if (k.toLowerCase().includes(query)) score += 1;
|
|
763
|
+
}
|
|
764
|
+
return { ...s, score };
|
|
765
|
+
})
|
|
766
|
+
.filter((s) => s.score > 0)
|
|
767
|
+
.sort((a, b) => b.score - a.score);
|
|
768
|
+
|
|
769
|
+
console.log(`\n🔍 "${process.argv.slice(4).join(' ')}" の検索結果\n`);
|
|
770
|
+
if (scored.length === 0) {
|
|
771
|
+
console.log('❌ 該当するスキルが見つかりませんでした。');
|
|
772
|
+
console.log('');
|
|
773
|
+
console.log('ヒント: 英語名(例: deep-learning, cancer-genomics)や');
|
|
774
|
+
console.log(' 日本語キーワード(例: 創薬, 機械学習)で検索してみてください。');
|
|
775
|
+
} else {
|
|
776
|
+
const top = scored.slice(0, 10);
|
|
777
|
+
for (const s of top) {
|
|
778
|
+
const desc = s.description ? s.description.split('\n')[0].substring(0, 60) : '';
|
|
779
|
+
console.log(` 📖 ${s.name}`);
|
|
780
|
+
if (desc) console.log(` ${desc}`);
|
|
781
|
+
console.log('');
|
|
782
|
+
}
|
|
783
|
+
if (scored.length > 10) {
|
|
784
|
+
console.log(` ... 他 ${scored.length - 10} 件`);
|
|
785
|
+
}
|
|
786
|
+
console.log(`合計 ${scored.length} 件がヒットしました。`);
|
|
787
|
+
console.log('詳細は `satori skill info <name>` で確認できます。');
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
function skillInfo() {
|
|
792
|
+
const name = process.argv[4];
|
|
793
|
+
if (!name) {
|
|
794
|
+
console.error('Error: スキル名を指定してください。');
|
|
795
|
+
console.log('Usage: satori skill info <name>');
|
|
796
|
+
console.log('スキル検索は `satori skill search <query>` を使ってください。');
|
|
797
|
+
process.exit(1);
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
const skillsDir = path.join(SOURCE_DIR, 'skills');
|
|
801
|
+
// scientific- プレフィックスを自動補完
|
|
802
|
+
const dirName = name.startsWith('scientific-') ? name : `scientific-${name}`;
|
|
803
|
+
const filePath = path.join(skillsDir, dirName, 'SKILL.md');
|
|
804
|
+
|
|
805
|
+
if (!fs.existsSync(filePath)) {
|
|
806
|
+
console.error(`Error: スキル "${name}" が見つかりません。`);
|
|
807
|
+
console.log('');
|
|
808
|
+
// 部分一致候補を提示
|
|
809
|
+
if (fs.existsSync(skillsDir)) {
|
|
810
|
+
const dirs = fs
|
|
811
|
+
.readdirSync(skillsDir)
|
|
812
|
+
.filter((d) => d.startsWith('scientific-') && d.includes(name))
|
|
813
|
+
.slice(0, 5);
|
|
814
|
+
if (dirs.length > 0) {
|
|
815
|
+
console.log('もしかして:');
|
|
816
|
+
for (const d of dirs) {
|
|
817
|
+
console.log(` - ${d.replace('scientific-', '')}`);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
process.exit(1);
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
825
|
+
const fm = parseFrontmatter(content);
|
|
826
|
+
const h1Match = content.match(/^# (.+)$/m);
|
|
827
|
+
const title = h1Match ? h1Match[1].trim() : dirName;
|
|
828
|
+
|
|
829
|
+
// 説明抽出
|
|
830
|
+
const descMatch = content.match(/^description:\s*\|?\s*\n([\s\S]*?)(?=\n\w|\n---)/m);
|
|
831
|
+
const description = descMatch ? descMatch[1].replace(/^\s+/gm, '').trim() : '';
|
|
832
|
+
|
|
833
|
+
// When to Use セクション抽出
|
|
834
|
+
const whenMatch = content.match(/^## When to Use\s*\n([\s\S]*?)(?=\n## )/m);
|
|
835
|
+
const whenToUse = whenMatch ? whenMatch[1].trim() : '';
|
|
836
|
+
|
|
837
|
+
// TU ツール
|
|
838
|
+
const tuKeyPattern = /`([A-Z][a-zA-Z]*_[a-z]+_[a-z_]+)`/g;
|
|
839
|
+
const tuKeys = new Set();
|
|
840
|
+
for (const m of content.matchAll(tuKeyPattern)) {
|
|
841
|
+
tuKeys.add(m[1]);
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
// tu_tools from frontmatter
|
|
845
|
+
const tuToolMatches = content.match(/^tu_tools:\s*\n([\s\S]*?)(?=\n---|\n[a-z])/m);
|
|
846
|
+
const tuToolNames = [];
|
|
847
|
+
if (tuToolMatches) {
|
|
848
|
+
const toolLines = tuToolMatches[1].matchAll(/name:\s*(.+)/g);
|
|
849
|
+
for (const m of toolLines) {
|
|
850
|
+
tuToolNames.push(m[1].trim());
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
// 関連パイプライン
|
|
855
|
+
const shortName = dirName.replace('scientific-', '');
|
|
856
|
+
const relatedPipelines = PIPELINES.filter((p) => p.skills.includes(shortName));
|
|
857
|
+
|
|
858
|
+
console.log(`\n📖 ${title}`);
|
|
859
|
+
console.log(` 名前: ${fm?.name || dirName}`);
|
|
860
|
+
if (description) {
|
|
861
|
+
console.log(` 説明: ${description}`);
|
|
862
|
+
}
|
|
863
|
+
console.log('');
|
|
864
|
+
|
|
865
|
+
if (whenToUse) {
|
|
866
|
+
console.log('── When to Use ──');
|
|
867
|
+
console.log(whenToUse);
|
|
868
|
+
console.log('');
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
if (tuKeys.size > 0 || tuToolNames.length > 0) {
|
|
872
|
+
console.log('── ToolUniverse 連携 ──');
|
|
873
|
+
if (tuToolNames.length > 0) {
|
|
874
|
+
for (const t of tuToolNames) {
|
|
875
|
+
console.log(` 🔧 ${t}`);
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
if (tuKeys.size > 0) {
|
|
879
|
+
console.log(` TU キー: ${[...tuKeys].join(', ')}`);
|
|
880
|
+
}
|
|
881
|
+
console.log('');
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
if (relatedPipelines.length > 0) {
|
|
885
|
+
console.log('── 関連パイプライン ──');
|
|
886
|
+
for (const p of relatedPipelines.slice(0, 5)) {
|
|
887
|
+
console.log(` 📋 #${p.id}: ${p.name}`);
|
|
888
|
+
}
|
|
889
|
+
if (relatedPipelines.length > 5) {
|
|
890
|
+
console.log(` ... 他 ${relatedPipelines.length - 5} 件`);
|
|
891
|
+
}
|
|
892
|
+
console.log('');
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
console.log(`ファイル: src/.github/skills/${dirName}/SKILL.md`);
|
|
896
|
+
}
|
|
897
|
+
|
|
483
898
|
switch (COMMAND) {
|
|
484
899
|
case 'init':
|
|
485
900
|
init();
|
|
486
901
|
break;
|
|
902
|
+
case 'skill':
|
|
903
|
+
if (SUBCOMMAND === 'search') {
|
|
904
|
+
skillSearch();
|
|
905
|
+
} else if (SUBCOMMAND === 'info') {
|
|
906
|
+
skillInfo();
|
|
907
|
+
} else {
|
|
908
|
+
console.error(`Unknown skill subcommand: ${SUBCOMMAND || '(none)'}`);
|
|
909
|
+
console.log('Usage: satori skill search <query> | satori skill info <name>');
|
|
910
|
+
process.exit(1);
|
|
911
|
+
}
|
|
912
|
+
break;
|
|
487
913
|
case 'pipeline':
|
|
488
914
|
if (SUBCOMMAND === 'suggest') {
|
|
489
915
|
pipelineSuggest();
|