@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.
Files changed (2) hide show
  1. package/bin/satori.js +430 -4
  2. 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
- console.log('\n📋 SATORI パイプライン一覧 (26 ドメインパイプライン)\n');
337
- for (const p of PIPELINES) {
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
- console.log('クロスドメイン (15), 産業特化 (5), 方法論特化 (4) パイプラインは');
343
- console.log('docs/SATORI_PIPELINE_EXAMPLES.md を参照してください。');
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();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nahisaho/satori",
3
- "version": "0.26.0",
3
+ "version": "0.27.1",
4
4
  "description": "SATORI — Agent Skills for Science. GitHub Copilot Agent Skills collection for scientific data analysis.",
5
5
  "main": "index.js",
6
6
  "bin": {