jupytermind 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/.github/skills/ai-chemistry-scientist/SKILL.md +97 -0
- package/.github/skills/ai-chemistry-scientist/manifest.json +156 -0
- package/.github/skills/ai-data-scientist/SKILL.md +330 -0
- package/.github/skills/ai-genomics-scientist/SKILL.md +98 -0
- package/.github/skills/ai-genomics-scientist/manifest.json +93 -0
- package/.github/skills/ai-materials-scientist/SKILL.md +51 -0
- package/.github/skills/ai-materials-scientist/manifest.json +58 -0
- package/.github/skills/ai-scientist/SKILL.md +69 -0
- package/.github/skills/ai-scientist/manifest.json +61 -0
- package/.github/skills/ai-structural-biology-scientist/SKILL.md +67 -0
- package/.github/skills/ai-structural-biology-scientist/manifest.json +72 -0
- package/.github/skills/japanese-prose/NOTICE.md +17 -0
- package/.github/skills/japanese-prose/SKILL.md +111 -0
- package/.github/skills/japanese-prose/references/review-workflow.md +50 -0
- package/.github/skills/japanese-prose/references/scoring.md +24 -0
- package/.github/skills/japanese-prose/references/writing-guidelines.md +60 -0
- package/.github/skills/japanese-prose/scripts/core.py +192 -0
- package/.github/skills/japanese-prose/scripts/fixtures/natural.md +5 -0
- package/.github/skills/japanese-prose/scripts/fixtures/unnatural.md +5 -0
- package/.github/skills/japanese-prose/scripts/lint.py +378 -0
- package/.github/skills/japanese-prose/scripts/outline.py +68 -0
- package/.github/skills/japanese-prose/scripts/terms.py +112 -0
- package/.github/skills/japanese-prose/scripts/test_engine.py +117 -0
- package/.github/skills/presentation-planner/SKILL.md +257 -0
- package/.github/skills/presentation-planner/assets/design-templates/data-report.yaml +97 -0
- package/.github/skills/presentation-planner/assets/design-templates/executive-proposal.yaml +92 -0
- package/.github/skills/presentation-planner/assets/design-templates/technical-briefing.yaml +96 -0
- package/.github/skills/presentation-planner/assets/scenario-templates/data-report.md +47 -0
- package/.github/skills/presentation-planner/assets/scenario-templates/executive-decision.md +43 -0
- package/.github/skills/presentation-planner/assets/scenario-templates/technical-briefing.md +45 -0
- package/.github/skills/presentation-planner/references/customizing-design-templates.md +160 -0
- package/.github/skills/presentation-planner/references/design-spec-schema.md +72 -0
- package/.github/skills/presentation-planner/references/handoff-contract.md +49 -0
- package/.github/skills/presentation-planner/references/responsibility-boundary.md +32 -0
- package/.github/skills/presentation-planner/references/scenario-templates.md +55 -0
- package/.github/skills/tech-writer/SKILL.md +434 -0
- package/.github/skills/tech-writer/assets/templates/blueprint.md +187 -0
- package/.github/skills/tech-writer/assets/templates/design-doc.md +29 -0
- package/.github/skills/tech-writer/assets/templates/migration-plan.md +173 -0
- package/.github/skills/tech-writer/assets/templates/operations-runbook.md +202 -0
- package/.github/skills/tech-writer/assets/templates/pr-description.md +23 -0
- package/.github/skills/tech-writer/assets/templates/qiita.md +44 -0
- package/.github/skills/tech-writer/assets/templates/readme.md +38 -0
- package/.github/skills/tech-writer/assets/templates/requirements-definition.md +170 -0
- package/.github/skills/tech-writer/assets/templates/rfi.md +113 -0
- package/.github/skills/tech-writer/assets/templates/rfp.md +180 -0
- package/.github/skills/tech-writer/assets/templates/security-design.md +167 -0
- package/.github/skills/tech-writer/assets/templates/system-design.md +220 -0
- package/.github/skills/tech-writer/assets/templates/technical-proposal.md +112 -0
- package/.github/skills/tech-writer/assets/templates/test-plan.md +153 -0
- package/.github/skills/tech-writer/assets/templates/user-manual.md +22 -0
- package/.github/skills/tech-writer/assets/templates/white-paper.md +192 -0
- package/.github/skills/tech-writer/references/doctypes/api-docs.md +33 -0
- package/.github/skills/tech-writer/references/doctypes/blueprint.md +81 -0
- package/.github/skills/tech-writer/references/doctypes/code-comments.md +39 -0
- package/.github/skills/tech-writer/references/doctypes/design-doc.md +42 -0
- package/.github/skills/tech-writer/references/doctypes/migration-plan.md +63 -0
- package/.github/skills/tech-writer/references/doctypes/operations-runbook.md +63 -0
- package/.github/skills/tech-writer/references/doctypes/pr-commit.md +82 -0
- package/.github/skills/tech-writer/references/doctypes/qiita.md +75 -0
- package/.github/skills/tech-writer/references/doctypes/readme.md +43 -0
- package/.github/skills/tech-writer/references/doctypes/release-notes.md +30 -0
- package/.github/skills/tech-writer/references/doctypes/requirements-definition.md +61 -0
- package/.github/skills/tech-writer/references/doctypes/rfi.md +43 -0
- package/.github/skills/tech-writer/references/doctypes/rfp.md +46 -0
- package/.github/skills/tech-writer/references/doctypes/security-design.md +71 -0
- package/.github/skills/tech-writer/references/doctypes/system-design.md +74 -0
- package/.github/skills/tech-writer/references/doctypes/technical-proposal.md +49 -0
- package/.github/skills/tech-writer/references/doctypes/test-plan.md +67 -0
- package/.github/skills/tech-writer/references/doctypes/user-manual.md +58 -0
- package/.github/skills/tech-writer/references/doctypes/white-paper.md +84 -0
- package/.github/skills/tech-writer/references/doctypes/zenn.md +66 -0
- package/.github/skills/tech-writer/references/japanese-prose-optimization.md +110 -0
- package/.github/skills/tech-writer/references/style-constitution.md +104 -0
- package/.github/skills/tech-writer/scripts/lint.py +412 -0
- package/LICENSE +21 -0
- package/README.md +92 -0
- package/bin/ai-data-scientist.js +123 -0
- package/package.json +41 -0
- package/pyproject.toml +45 -0
- package/src/ai_chemistry_scientist/__init__.py +0 -0
- package/src/ai_chemistry_scientist/admet_prediction.py +71 -0
- package/src/ai_chemistry_scientist/bioactivity_classification.py +73 -0
- package/src/ai_chemistry_scientist/data/sample_molecules.csv +21 -0
- package/src/ai_chemistry_scientist/dispatch.py +369 -0
- package/src/ai_chemistry_scientist/docking_score.py +97 -0
- package/src/ai_chemistry_scientist/drug_likeness_rules.py +84 -0
- package/src/ai_chemistry_scientist/evidence.py +41 -0
- package/src/ai_chemistry_scientist/molecular_descriptors.py +97 -0
- package/src/ai_chemistry_scientist/molecular_formula_mass.py +40 -0
- package/src/ai_chemistry_scientist/molecular_similarity.py +78 -0
- package/src/ai_chemistry_scientist/qsar_modeling.py +105 -0
- package/src/ai_chemistry_scientist/salt_standardization.py +81 -0
- package/src/ai_chemistry_scientist/structural_alerts.py +76 -0
- package/src/ai_chemistry_scientist/structure_format_conversion.py +84 -0
- package/src/ai_chemistry_scientist/validation.py +70 -0
- package/src/ai_data_scientist/__init__.py +0 -0
- package/src/ai_data_scientist/analysis_assumptions.py +121 -0
- package/src/ai_data_scientist/anomaly_detection.py +39 -0
- package/src/ai_data_scientist/automl.py +109 -0
- package/src/ai_data_scientist/cleaning.py +56 -0
- package/src/ai_data_scientist/cli.py +90 -0
- package/src/ai_data_scientist/clustering.py +54 -0
- package/src/ai_data_scientist/dashboard.py +33 -0
- package/src/ai_data_scientist/data_definition.py +100 -0
- package/src/ai_data_scientist/data_quality.py +164 -0
- package/src/ai_data_scientist/dataset_validation.py +135 -0
- package/src/ai_data_scientist/dependency_pins.py +60 -0
- package/src/ai_data_scientist/eda.py +82 -0
- package/src/ai_data_scientist/experiment_evaluation.py +635 -0
- package/src/ai_data_scientist/explainability.py +340 -0
- package/src/ai_data_scientist/feature_engineering.py +163 -0
- package/src/ai_data_scientist/gate_config.py +32 -0
- package/src/ai_data_scientist/ingestion.py +127 -0
- package/src/ai_data_scientist/insight_engine.py +180 -0
- package/src/ai_data_scientist/japanese_nlp.py +43 -0
- package/src/ai_data_scientist/jupyter_launcher.py +137 -0
- package/src/ai_data_scientist/jupyter_mcp_client.py +94 -0
- package/src/ai_data_scientist/language_router.py +28 -0
- package/src/ai_data_scientist/lifecycle.py +221 -0
- package/src/ai_data_scientist/mcp_gateway.py +113 -0
- package/src/ai_data_scientist/mcp_runtime.py +194 -0
- package/src/ai_data_scientist/mcp_transport.py +53 -0
- package/src/ai_data_scientist/ml_modeling.py +451 -0
- package/src/ai_data_scientist/model_tuning.py +104 -0
- package/src/ai_data_scientist/notebook_audit.py +574 -0
- package/src/ai_data_scientist/project_manager.py +243 -0
- package/src/ai_data_scientist/report_export.py +73 -0
- package/src/ai_data_scientist/sensitivity.py +445 -0
- package/src/ai_data_scientist/signal_analysis.py +201 -0
- package/src/ai_data_scientist/skill_packaging.py +40 -0
- package/src/ai_data_scientist/stats_analysis.py +88 -0
- package/src/ai_data_scientist/text_nlp.py +44 -0
- package/src/ai_data_scientist/timeseries.py +68 -0
- package/src/ai_data_scientist/visualization.py +708 -0
- package/src/ai_genomics_scientist/__init__.py +1 -0
- package/src/ai_genomics_scientist/differential_expression.py +147 -0
- package/src/ai_genomics_scientist/dispatch.py +267 -0
- package/src/ai_genomics_scientist/evidence.py +45 -0
- package/src/ai_genomics_scientist/gene_set_enrichment.py +76 -0
- package/src/ai_genomics_scientist/sequence_alignment.py +97 -0
- package/src/ai_genomics_scientist/sequence_features.py +111 -0
- package/src/ai_genomics_scientist/splice_site_scoring.py +66 -0
- package/src/ai_genomics_scientist/validation.py +83 -0
- package/src/ai_genomics_scientist/variant_effect.py +147 -0
- package/src/ai_genomics_scientist/variant_pathogenicity.py +125 -0
- package/src/ai_materials_scientist/__init__.py +0 -0
- package/src/ai_materials_scientist/calphad.py +117 -0
- package/src/ai_materials_scientist/classical_monte_carlo.py +165 -0
- package/src/ai_materials_scientist/crystal_plasticity.py +184 -0
- package/src/ai_materials_scientist/dispatch.py +100 -0
- package/src/ai_materials_scientist/evidence.py +84 -0
- package/src/ai_materials_scientist/fem.py +279 -0
- package/src/ai_materials_scientist/kinetic_monte_carlo.py +145 -0
- package/src/ai_materials_scientist/molecular_dynamics.py +240 -0
- package/src/ai_materials_scientist/phase_field.py +167 -0
- package/src/ai_materials_scientist/validation.py +70 -0
- package/src/ai_scientist/__init__.py +1 -0
- package/src/ai_scientist/completion_gate.py +15 -0
- package/src/ai_scientist/data_analysis.py +46 -0
- package/src/ai_scientist/evidence_registry.py +99 -0
- package/src/ai_scientist/experimental_design.py +20 -0
- package/src/ai_scientist/language.py +14 -0
- package/src/ai_scientist/latex_renderer.py +41 -0
- package/src/ai_scientist/literature_review.py +37 -0
- package/src/ai_scientist/manifest.py +87 -0
- package/src/ai_scientist/manuscript.py +94 -0
- package/src/ai_scientist/mcp_config.py +76 -0
- package/src/ai_scientist/mcp_external.py +42 -0
- package/src/ai_scientist/mcp_failures.py +23 -0
- package/src/ai_scientist/mcp_gateway.py +38 -0
- package/src/ai_scientist/mcp_managed.py +180 -0
- package/src/ai_scientist/npm_packaging.py +49 -0
- package/src/ai_scientist/orchestrator.py +133 -0
- package/src/ai_scientist/peer_review.py +60 -0
- package/src/ai_scientist/phase_gate.py +74 -0
- package/src/ai_scientist/phase_state.py +230 -0
- package/src/ai_scientist/presentation.py +56 -0
- package/src/ai_scientist/project_config.py +31 -0
- package/src/ai_scientist/project_handle.py +74 -0
- package/src/ai_scientist/reproducibility.py +20 -0
- package/src/ai_scientist/research_planning.py +20 -0
- package/src/ai_scientist/skill_invocation.py +21 -0
- package/src/ai_scientist/tdd_gate.py +99 -0
- package/src/ai_structural_biology_scientist/__init__.py +0 -0
- package/src/ai_structural_biology_scientist/contact_map.py +87 -0
- package/src/ai_structural_biology_scientist/dispatch.py +269 -0
- package/src/ai_structural_biology_scientist/evidence.py +43 -0
- package/src/ai_structural_biology_scientist/hydrophobicity.py +101 -0
- package/src/ai_structural_biology_scientist/protein_docking_score.py +104 -0
- package/src/ai_structural_biology_scientist/secondary_structure.py +95 -0
- package/src/ai_structural_biology_scientist/structural_similarity.py +74 -0
- package/src/ai_structural_biology_scientist/validation.py +100 -0
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# /// script
|
|
3
|
+
# requires-python = ">=3.9"
|
|
4
|
+
# dependencies = []
|
|
5
|
+
# ///
|
|
6
|
+
"""tech-writer skill: a lint script that mechanically checks a technical
|
|
7
|
+
document's *structure* and Markdown rendering safety.
|
|
8
|
+
|
|
9
|
+
Where japanese-prose's GiNZA lint detects sentence-level naturalness
|
|
10
|
+
(vocabulary, rhythm), this script detects structural problems specific to
|
|
11
|
+
technical documents plus Markdown syntax patterns that render inconsistently
|
|
12
|
+
(heading hierarchy, code examples, leftover placeholders, suspicious links,
|
|
13
|
+
and bold delimiters touching prose). The two scripts intentionally don't
|
|
14
|
+
overlap in scope.
|
|
15
|
+
|
|
16
|
+
Findings are flags, not mandates: exit code is always 0 regardless of the
|
|
17
|
+
finding count (it's a lint, so it shouldn't block CI). Exit code 1 is
|
|
18
|
+
reserved for the input file being missing or unreadable.
|
|
19
|
+
|
|
20
|
+
Pass --atomic when linting a commit message, PR description, issue report,
|
|
21
|
+
code comment/docstring, or a single release-notes entry: these atomic
|
|
22
|
+
artifacts follow their own doctype skeleton (see style-constitution.md's
|
|
23
|
+
scope note) and legitimately have no H1 title, so --atomic skips the
|
|
24
|
+
living-document intro-paragraph check that would otherwise misfire on
|
|
25
|
+
them (e.g. assets/templates/pr-description.md lints clean with --atomic).
|
|
26
|
+
|
|
27
|
+
Usage:
|
|
28
|
+
uv run scripts/lint.py <file>
|
|
29
|
+
uv run scripts/lint.py --json <file>
|
|
30
|
+
uv run scripts/lint.py --atomic <file>
|
|
31
|
+
"""
|
|
32
|
+
from __future__ import annotations
|
|
33
|
+
|
|
34
|
+
import argparse
|
|
35
|
+
import json
|
|
36
|
+
import re
|
|
37
|
+
import sys
|
|
38
|
+
from dataclasses import dataclass, field
|
|
39
|
+
from pathlib import Path
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@dataclass
|
|
43
|
+
class Finding:
|
|
44
|
+
line: int
|
|
45
|
+
category: str
|
|
46
|
+
message: str
|
|
47
|
+
snippet: str = ""
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@dataclass
|
|
51
|
+
class LintResult:
|
|
52
|
+
file: str
|
|
53
|
+
findings: list = field(default_factory=list)
|
|
54
|
+
|
|
55
|
+
def to_dict(self) -> dict:
|
|
56
|
+
return {
|
|
57
|
+
"file": self.file,
|
|
58
|
+
"finding_count": len(self.findings),
|
|
59
|
+
"findings": [f.__dict__ for f in self.findings],
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
HEADING_RE = re.compile(r"^(#{1,6})\s+(.*)$")
|
|
64
|
+
# Generic heading labels that read as content-free in either language.
|
|
65
|
+
GENERIC_HEADINGS = {
|
|
66
|
+
"overview", "introduction", "usage", "notes", "misc", "others",
|
|
67
|
+
"概要", "はじめに", "使い方", "使用方法", "注意点", "注意事項", "その他", "補足",
|
|
68
|
+
}
|
|
69
|
+
# A fence marker is 3+ backticks or 3+ tildes, optionally followed by an
|
|
70
|
+
# info string (e.g. the language). CommonMark requires the closing fence to
|
|
71
|
+
# use the same character and be at least as long as the opener, with no
|
|
72
|
+
# info string of its own.
|
|
73
|
+
FENCE_RE = re.compile(r"^(`{3,}|~{3,})(.*)$")
|
|
74
|
+
PLACEHOLDER_RE = re.compile(r"\b(TODO|FIXME|TBD|XXX)\b", re.IGNORECASE)
|
|
75
|
+
MD_LINK_RE = re.compile(r"\[([^\]]*)\]\(([^)]+)\)")
|
|
76
|
+
INLINE_CODE_RE = re.compile(r"`[^`\n]+`")
|
|
77
|
+
BOLD_RE = re.compile(r"(?<![\\*])\*\*(?!\s)(.+?)(?<!\s)\*\*(?!\*)")
|
|
78
|
+
NON_PARAGRAPH_RE = re.compile(
|
|
79
|
+
r"^(?:[-*+]\s|\d+[.)]\s|>|<!--|\|)|^(?:-{3,}|\*{3,}|_{3,})$"
|
|
80
|
+
)
|
|
81
|
+
# CommonMark indented code block: 4+ leading spaces or a leading tab.
|
|
82
|
+
INDENTED_CODE_RE = re.compile(r"^(?: {4,}|\t)\S")
|
|
83
|
+
# A YAML frontmatter field named "title" with a non-empty value, e.g. a
|
|
84
|
+
# Zenn/Qiita article's `title: "..."` (the platform renders this as the
|
|
85
|
+
# page title, so the body conventionally has no in-body '#' heading).
|
|
86
|
+
TITLE_FIELD_RE = re.compile(r'^title:\s*(["\']?)\S')
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def strip_inline_code(line: str) -> str:
|
|
90
|
+
"""Blank out inline `code span` contents so placeholder/link checks
|
|
91
|
+
don't fire on tokens that are only being *mentioned* as code, not left
|
|
92
|
+
unresolved in prose (e.g. a doctype guide showing `TODO(#123): ...` as
|
|
93
|
+
an example of the correct form)."""
|
|
94
|
+
return INLINE_CODE_RE.sub(lambda m: " " * len(m.group(0)), line)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def parse_fences(lines: list) -> tuple:
|
|
98
|
+
"""Scan for fenced code blocks.
|
|
99
|
+
|
|
100
|
+
Returns (findings, fence_mask) where fence_mask[i] is True when line i
|
|
101
|
+
(0-based) is part of a fenced code block (opening/closing marker or
|
|
102
|
+
content in between). Other checks should skip masked lines so that
|
|
103
|
+
headings, TODOs, or links written *inside* example code blocks aren't
|
|
104
|
+
mistaken for real document structure.
|
|
105
|
+
"""
|
|
106
|
+
findings = []
|
|
107
|
+
fence_mask = [False] * len(lines)
|
|
108
|
+
open_char = None
|
|
109
|
+
open_len = 0
|
|
110
|
+
open_line = None
|
|
111
|
+
|
|
112
|
+
def fence_match(raw_line):
|
|
113
|
+
# CommonMark/GFM only recognizes a fence indented by at most three
|
|
114
|
+
# spaces; four or more spaces (or a leading tab, which expands to
|
|
115
|
+
# 4+ columns) is indented code, not a fence.
|
|
116
|
+
expanded = raw_line.expandtabs(4)
|
|
117
|
+
indent = len(expanded) - len(expanded.lstrip(" "))
|
|
118
|
+
if indent > 3:
|
|
119
|
+
return None
|
|
120
|
+
return FENCE_RE.match(raw_line.strip())
|
|
121
|
+
|
|
122
|
+
for i, line in enumerate(lines):
|
|
123
|
+
m = fence_match(line)
|
|
124
|
+
if open_char is None:
|
|
125
|
+
if m:
|
|
126
|
+
marker, info = m.group(1), m.group(2).strip()
|
|
127
|
+
open_char, open_len, open_line = marker[0], len(marker), i + 1
|
|
128
|
+
fence_mask[i] = True
|
|
129
|
+
if not info:
|
|
130
|
+
findings.append(
|
|
131
|
+
Finding(
|
|
132
|
+
line=i + 1,
|
|
133
|
+
category="code_fence_no_lang",
|
|
134
|
+
message="Code block has no language tag (recommended for syntax highlighting and copy detection).",
|
|
135
|
+
snippet=line.strip(),
|
|
136
|
+
)
|
|
137
|
+
)
|
|
138
|
+
continue
|
|
139
|
+
|
|
140
|
+
fence_mask[i] = True
|
|
141
|
+
if m:
|
|
142
|
+
marker, info = m.group(1), m.group(2).strip()
|
|
143
|
+
is_closing = marker[0] == open_char and len(marker) >= open_len and not info
|
|
144
|
+
if is_closing:
|
|
145
|
+
open_char, open_len, open_line = None, 0, None
|
|
146
|
+
|
|
147
|
+
if open_char is not None:
|
|
148
|
+
findings.append(
|
|
149
|
+
Finding(
|
|
150
|
+
line=open_line or 0,
|
|
151
|
+
category="unclosed_code_fence",
|
|
152
|
+
message="A code block may not be closed.",
|
|
153
|
+
)
|
|
154
|
+
)
|
|
155
|
+
return findings, fence_mask
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def check_heading_hierarchy(lines: list, fence_mask: list) -> list:
|
|
159
|
+
findings = []
|
|
160
|
+
prev_level = 0
|
|
161
|
+
for i, line in enumerate(lines):
|
|
162
|
+
if fence_mask[i]:
|
|
163
|
+
continue
|
|
164
|
+
m = HEADING_RE.match(line)
|
|
165
|
+
if not m:
|
|
166
|
+
continue
|
|
167
|
+
level = len(m.group(1))
|
|
168
|
+
text = m.group(2).strip()
|
|
169
|
+
if prev_level and level > prev_level + 1:
|
|
170
|
+
findings.append(
|
|
171
|
+
Finding(
|
|
172
|
+
line=i + 1,
|
|
173
|
+
category="heading_skip",
|
|
174
|
+
message=f"Heading level jumps from H{prev_level} to H{level}.",
|
|
175
|
+
snippet=line.strip(),
|
|
176
|
+
)
|
|
177
|
+
)
|
|
178
|
+
stripped = text.rstrip("::").strip().lower()
|
|
179
|
+
if stripped in GENERIC_HEADINGS:
|
|
180
|
+
findings.append(
|
|
181
|
+
Finding(
|
|
182
|
+
line=i + 1,
|
|
183
|
+
category="generic_heading",
|
|
184
|
+
message="Generic heading label; make it preview the content instead (structure constitution rule 2).",
|
|
185
|
+
snippet=line.strip(),
|
|
186
|
+
)
|
|
187
|
+
)
|
|
188
|
+
prev_level = level
|
|
189
|
+
return findings
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def check_placeholders(lines: list, fence_mask: list) -> list:
|
|
193
|
+
findings = []
|
|
194
|
+
for i, line in enumerate(lines):
|
|
195
|
+
if fence_mask[i]:
|
|
196
|
+
continue
|
|
197
|
+
checked = strip_inline_code(line)
|
|
198
|
+
for m in PLACEHOLDER_RE.finditer(checked):
|
|
199
|
+
# A justified/tracked marker like "TODO(#123): ..." documents a
|
|
200
|
+
# reason and a tracking reference, which is exactly what this
|
|
201
|
+
# skill's own guidance asks for — don't flag that form. An empty
|
|
202
|
+
# "TODO()" carries no reason at all, so it still gets flagged.
|
|
203
|
+
if checked[m.end():m.end() + 1] == "(":
|
|
204
|
+
close_idx = checked.find(")", m.end())
|
|
205
|
+
content = checked[m.end() + 1:close_idx] if close_idx != -1 else ""
|
|
206
|
+
if content.strip():
|
|
207
|
+
continue
|
|
208
|
+
findings.append(
|
|
209
|
+
Finding(
|
|
210
|
+
line=i + 1,
|
|
211
|
+
category="placeholder",
|
|
212
|
+
message=f"Unresolved placeholder '{m.group(1)}' remains without a reason/tracking reference; resolve before publishing.",
|
|
213
|
+
snippet=line.strip(),
|
|
214
|
+
)
|
|
215
|
+
)
|
|
216
|
+
return findings
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def check_links(lines: list, fence_mask: list) -> list:
|
|
220
|
+
findings = []
|
|
221
|
+
for i, line in enumerate(lines):
|
|
222
|
+
if fence_mask[i]:
|
|
223
|
+
continue
|
|
224
|
+
checked = strip_inline_code(line)
|
|
225
|
+
for m in MD_LINK_RE.finditer(checked):
|
|
226
|
+
text, target = m.group(1), m.group(2)
|
|
227
|
+
if not text.strip():
|
|
228
|
+
findings.append(
|
|
229
|
+
Finding(
|
|
230
|
+
line=i + 1,
|
|
231
|
+
category="empty_link_text",
|
|
232
|
+
message="Link text is empty. Avoid content-free link text like 'here'/'こちら' too.",
|
|
233
|
+
snippet=line.strip(),
|
|
234
|
+
)
|
|
235
|
+
)
|
|
236
|
+
if target.strip() in ("#", "", "javascript:void(0)"):
|
|
237
|
+
findings.append(
|
|
238
|
+
Finding(
|
|
239
|
+
line=i + 1,
|
|
240
|
+
category="dead_link_placeholder",
|
|
241
|
+
message="Link target is still an unset placeholder.",
|
|
242
|
+
snippet=line.strip(),
|
|
243
|
+
)
|
|
244
|
+
)
|
|
245
|
+
return findings
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def check_bold_spacing(lines: list, fence_mask: list) -> list:
|
|
249
|
+
"""Flag bold delimiters with missing or non-ASCII surrounding spaces.
|
|
250
|
+
|
|
251
|
+
Some Markdown renderers fail to recognize strong emphasis when `**`
|
|
252
|
+
directly adjoins Japanese or other word characters. Punctuation and
|
|
253
|
+
line boundaries do not need padding. When padding is present, it must
|
|
254
|
+
be an ASCII half-width space rather than a tab or Unicode space.
|
|
255
|
+
"""
|
|
256
|
+
findings = []
|
|
257
|
+
for i, line in enumerate(lines):
|
|
258
|
+
if fence_mask[i]:
|
|
259
|
+
continue
|
|
260
|
+
checked = strip_inline_code(line)
|
|
261
|
+
for match in BOLD_RE.finditer(checked):
|
|
262
|
+
before = checked[match.start() - 1] if match.start() else ""
|
|
263
|
+
after = checked[match.end()] if match.end() < len(checked) else ""
|
|
264
|
+
if (before and (before.isalnum() or before == "_")) or (
|
|
265
|
+
after and (after.isalnum() or after == "_")
|
|
266
|
+
) or (
|
|
267
|
+
before and before.isspace() and before != " "
|
|
268
|
+
) or (
|
|
269
|
+
after and after.isspace() and after != " "
|
|
270
|
+
):
|
|
271
|
+
findings.append(
|
|
272
|
+
Finding(
|
|
273
|
+
line=i + 1,
|
|
274
|
+
category="bold_spacing",
|
|
275
|
+
message=(
|
|
276
|
+
"Use ASCII half-width spaces immediately before "
|
|
277
|
+
"and after '**...**' when it is embedded in prose; "
|
|
278
|
+
"do not use full-width or other Unicode spaces."
|
|
279
|
+
),
|
|
280
|
+
snippet=line.strip(),
|
|
281
|
+
)
|
|
282
|
+
)
|
|
283
|
+
return findings
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def check_intro_paragraph(lines: list, fence_mask: list) -> list:
|
|
287
|
+
"""Check that the document opens with a non-empty H1 title immediately
|
|
288
|
+
followed by a genuine body paragraph.
|
|
289
|
+
|
|
290
|
+
This is a heuristic proxy for structure constitution rule 1 ("say what
|
|
291
|
+
this is and the outcome up front"). The very first non-blank line after
|
|
292
|
+
the title must be plain prose — a list item, blockquote, HTML comment,
|
|
293
|
+
table row, thematic break, another heading, a fenced code block, or
|
|
294
|
+
indented code does not count, even if real prose follows it further
|
|
295
|
+
down. A leading YAML frontmatter block (e.g. skill metadata, or a
|
|
296
|
+
platform frontmatter with its own `title:` field such as Zenn/Qiita) is
|
|
297
|
+
skipped before this check begins; fenced code is only skipped while
|
|
298
|
+
still searching for the title itself (a heading can't appear inside
|
|
299
|
+
one).
|
|
300
|
+
|
|
301
|
+
A frontmatter block that already carries a non-empty `title:` field
|
|
302
|
+
counts as satisfying the title requirement on its own — those platforms
|
|
303
|
+
render that field as the page/article title and conventionally don't
|
|
304
|
+
repeat it as an in-body '#' heading.
|
|
305
|
+
"""
|
|
306
|
+
start = 0
|
|
307
|
+
frontmatter_has_title = False
|
|
308
|
+
if lines and lines[0].strip() == "---":
|
|
309
|
+
for j in range(1, len(lines)):
|
|
310
|
+
if lines[j].strip() == "---":
|
|
311
|
+
start = j + 1
|
|
312
|
+
break
|
|
313
|
+
if TITLE_FIELD_RE.match(lines[j]):
|
|
314
|
+
frontmatter_has_title = True
|
|
315
|
+
state = "after_title" if frontmatter_has_title else "before_title"
|
|
316
|
+
for i, line in enumerate(lines):
|
|
317
|
+
if i < start:
|
|
318
|
+
continue
|
|
319
|
+
if state == "before_title" and fence_mask[i]:
|
|
320
|
+
continue
|
|
321
|
+
stripped = line.strip()
|
|
322
|
+
if not stripped:
|
|
323
|
+
continue
|
|
324
|
+
if state == "after_title" and (fence_mask[i] or INDENTED_CODE_RE.match(line)):
|
|
325
|
+
# Fenced or indented code right after the title isn't prose.
|
|
326
|
+
break
|
|
327
|
+
m = HEADING_RE.match(stripped)
|
|
328
|
+
if state == "before_title":
|
|
329
|
+
if m and len(m.group(1)) == 1 and m.group(2).strip():
|
|
330
|
+
state = "after_title"
|
|
331
|
+
continue
|
|
332
|
+
# Either the first heading isn't a non-empty top-level title,
|
|
333
|
+
# or non-heading content appeared before any title — either way
|
|
334
|
+
# there's nothing valid to anchor the check against.
|
|
335
|
+
break
|
|
336
|
+
# state == "after_title": the very next non-blank line decides it.
|
|
337
|
+
if m or NON_PARAGRAPH_RE.match(stripped):
|
|
338
|
+
break
|
|
339
|
+
return []
|
|
340
|
+
return [
|
|
341
|
+
Finding(
|
|
342
|
+
line=1,
|
|
343
|
+
category="missing_intro",
|
|
344
|
+
message="Document must open with a non-empty '#' title heading immediately followed by a plain-prose paragraph (not a list, blockquote, comment, table, code block, or another heading) stating what this is and the reader outcome (structure constitution rule 1).",
|
|
345
|
+
)
|
|
346
|
+
]
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def run_lint(path: Path, atomic: bool = False) -> LintResult:
|
|
350
|
+
text = path.read_text(encoding="utf-8")
|
|
351
|
+
lines = text.splitlines()
|
|
352
|
+
result = LintResult(file=str(path))
|
|
353
|
+
fence_findings, fence_mask = parse_fences(lines)
|
|
354
|
+
result.findings.extend(fence_findings)
|
|
355
|
+
result.findings.extend(check_heading_hierarchy(lines, fence_mask))
|
|
356
|
+
result.findings.extend(check_placeholders(lines, fence_mask))
|
|
357
|
+
result.findings.extend(check_links(lines, fence_mask))
|
|
358
|
+
result.findings.extend(check_bold_spacing(lines, fence_mask))
|
|
359
|
+
if not atomic:
|
|
360
|
+
# check_intro_paragraph assumes a living, multi-section document
|
|
361
|
+
# (H1 title + opening paragraph); atomic artifacts like a PR
|
|
362
|
+
# description or a single release-notes entry follow their own
|
|
363
|
+
# doctype skeleton instead (see style-constitution.md's scope
|
|
364
|
+
# note) and legitimately have no H1 at all.
|
|
365
|
+
result.findings.extend(check_intro_paragraph(lines, fence_mask))
|
|
366
|
+
result.findings.sort(key=lambda f: f.line)
|
|
367
|
+
return result
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
def main() -> int:
|
|
371
|
+
parser = argparse.ArgumentParser(description="tech-writer structural lint")
|
|
372
|
+
parser.add_argument("file", type=str, help="Target Markdown file")
|
|
373
|
+
parser.add_argument("--json", action="store_true", help="Output as JSON")
|
|
374
|
+
parser.add_argument(
|
|
375
|
+
"--atomic",
|
|
376
|
+
action="store_true",
|
|
377
|
+
help=(
|
|
378
|
+
"Lint as an atomic artifact (commit message, PR description, "
|
|
379
|
+
"issue report, code comment/docstring, a single release-notes "
|
|
380
|
+
"entry): skips the living-document intro-paragraph check, "
|
|
381
|
+
"which doesn't apply to these doctypes' own skeletons."
|
|
382
|
+
),
|
|
383
|
+
)
|
|
384
|
+
args = parser.parse_args()
|
|
385
|
+
|
|
386
|
+
path = Path(args.file)
|
|
387
|
+
if not path.is_file():
|
|
388
|
+
print(f"error: file not found: {path}", file=sys.stderr)
|
|
389
|
+
return 1
|
|
390
|
+
|
|
391
|
+
try:
|
|
392
|
+
result = run_lint(path, atomic=args.atomic)
|
|
393
|
+
except OSError as e:
|
|
394
|
+
print(f"error: failed to read file: {e}", file=sys.stderr)
|
|
395
|
+
return 1
|
|
396
|
+
|
|
397
|
+
if args.json:
|
|
398
|
+
print(json.dumps(result.to_dict(), ensure_ascii=False, indent=2))
|
|
399
|
+
else:
|
|
400
|
+
if not result.findings:
|
|
401
|
+
print(f"{path}: no structural findings.")
|
|
402
|
+
else:
|
|
403
|
+
print(f"{path}: {len(result.findings)} finding(s)")
|
|
404
|
+
for f in result.findings:
|
|
405
|
+
print(f" L{f.line} [{f.category}] {f.message}")
|
|
406
|
+
if f.snippet:
|
|
407
|
+
print(f" > {f.snippet}")
|
|
408
|
+
return 0
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
if __name__ == "__main__":
|
|
412
|
+
sys.exit(main())
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 nahisaho
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Jupytermind:AI Scientist Skill Suite — Jupyter MCP Copilot Agent Skills
|
|
2
|
+
|
|
3
|
+
[日本語 README](README-ja.md)
|
|
4
|
+
|
|
5
|
+
A collection of GitHub Copilot Agent Skills for natural-language (Japanese/
|
|
6
|
+
English) scientific computing over Jupyter via the Jupyter MCP (Datalayer
|
|
7
|
+
`jupyter-mcp-server`). Every reasoning-based insight is recorded in the
|
|
8
|
+
project notebook alongside the executed cell that provides its evidence.
|
|
9
|
+
Built with SDD (`musubix3`) and TDD (pytest).
|
|
10
|
+
|
|
11
|
+
## Skills included
|
|
12
|
+
|
|
13
|
+
| Skill | Scope |
|
|
14
|
+
| --- | --- |
|
|
15
|
+
| `ai-data-scientist` | Load, clean, explore, analyze, visualize, and draw insights from datasets via Jupyter (MVP + ML extension). |
|
|
16
|
+
| `ai-chemistry-scientist` | Cheminformatics: molecular descriptors, ADMET heuristic screening, QSAR modeling, similarity search, docking-score heuristics, drug-likeness/structural-alert screening, formula/mass calculation, SMILES standardization and format conversion. |
|
|
17
|
+
| `ai-genomics-scientist` | Computational genomics: sequence feature analysis, variant-effect heuristic annotation, splice-site strength scoring, gene-set enrichment analysis, pairwise sequence alignment. |
|
|
18
|
+
| `ai-materials-scientist` | Materials-science simulation: phase-field microstructure evolution, molecular dynamics, classical/kinetic Monte Carlo, crystal plasticity, a simplified FEM solver, and a simplified binary CALPHAD phase diagram. |
|
|
19
|
+
| `ai-structural-biology-scientist` | Structural biology: secondary-structure and hydrophobicity/burial heuristics, protein-protein docking-score heuristics, RMSD-based structural similarity, residue contact-map heuristics. |
|
|
20
|
+
| `ai-scientist` | End-to-end single-project research guidance: planning, literature review, experimental design, data analysis, manuscript writing, peer review, reproducibility checks, and presentation. |
|
|
21
|
+
| `tech-writer` | Structures and polishes technical documents: READMEs, design docs/ADRs, API references, PR descriptions, release notes, user manuals, code comments. |
|
|
22
|
+
| `japanese-prose` | Improves Japanese prose quality using GiNZA-based diagnostics (kotonoha). |
|
|
23
|
+
| `presentation-planner` | Plans PPTX content structure and design handoff (does not generate the file itself). |
|
|
24
|
+
| `sdd-*` (`sdd-change`, `sdd-requirements`, `sdd-design`, `sdd-implementation`, `sdd-quality`, `sdd-traceability`, `sdd-knowledge`, `sdd-formal-codegraph`, `sdd-issue-report`) | The Specification-Driven-Development workflow (`musubix3`) used to build and evolve every skill above. |
|
|
25
|
+
|
|
26
|
+
See each skill's `.github/skills/<name>/SKILL.md` for its full invocation
|
|
27
|
+
instructions and trigger phrases.
|
|
28
|
+
|
|
29
|
+
## Setup
|
|
30
|
+
|
|
31
|
+
Install the skill from the npm registry (no need to clone this repo):
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
npm install jupytermind
|
|
35
|
+
npx ai-data-scientist doctor
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Or, for a local checkout of this repo:
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
npm install
|
|
42
|
+
npx ai-data-scientist doctor
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
The Python environment (virtualenv + dependencies) is set up automatically
|
|
46
|
+
on first CLI invocation in both cases.
|
|
47
|
+
|
|
48
|
+
`npm install` itself has no dependencies and does not use `postinstall`
|
|
49
|
+
(since npm v12, install scripts of dependencies are disabled by default
|
|
50
|
+
and require `npm approve-scripts`). Instead, `bin/ai-data-scientist.js`
|
|
51
|
+
creates a `.venv` and installs the dependencies from `pyproject.toml` on
|
|
52
|
+
first CLI invocation. Subsequent invocations are cached and start fast.
|
|
53
|
+
|
|
54
|
+
To set up the Python environment manually:
|
|
55
|
+
|
|
56
|
+
```sh
|
|
57
|
+
python3 -m venv .venv
|
|
58
|
+
.venv/bin/pip install -e .
|
|
59
|
+
.venv/bin/pytest
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### PDF export prerequisites
|
|
63
|
+
|
|
64
|
+
`report_export.export_report(..., report_format="pdf")` uses nbconvert's
|
|
65
|
+
`PDFExporter`, which shells out to a system `xelatex` binary. This is **not**
|
|
66
|
+
installed by `pip`/`npm` and must be provided separately, e.g.:
|
|
67
|
+
|
|
68
|
+
```sh
|
|
69
|
+
# Debian/Ubuntu
|
|
70
|
+
sudo apt-get install texlive-xetex texlive-fonts-recommended
|
|
71
|
+
|
|
72
|
+
# macOS
|
|
73
|
+
brew install --cask mactex-no-gui
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
If `xelatex` is not on `PATH`, PDF export raises a `RuntimeError` pointing
|
|
77
|
+
back to this section; use `report_format="html"` if you do not need PDF
|
|
78
|
+
output.
|
|
79
|
+
|
|
80
|
+
## Skills
|
|
81
|
+
|
|
82
|
+
See `.github/skills/<skill-name>/SKILL.md` for each skill's invocation
|
|
83
|
+
instructions and `.musubix/features/<feature-slug>/requirements.md` for its
|
|
84
|
+
authoritative requirement set.
|
|
85
|
+
|
|
86
|
+
## License
|
|
87
|
+
|
|
88
|
+
MIT License — see [LICENSE](LICENSE).
|
|
89
|
+
|
|
90
|
+
## Changelog
|
|
91
|
+
|
|
92
|
+
See [CHANGELOG.md](CHANGELOG.md) for release notes.
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* npm bootstrap entrypoint for the ai-data-scientist Copilot Agent Skill.
|
|
6
|
+
*
|
|
7
|
+
* This script intentionally does NOT run as an npm "postinstall" script:
|
|
8
|
+
* starting with npm v12, install-lifecycle scripts from dependencies are
|
|
9
|
+
* disabled by default and require each consumer to run
|
|
10
|
+
* `npm approve-scripts`, which is not a realistic onboarding step for a
|
|
11
|
+
* published skill package. Instead, environment setup happens lazily on
|
|
12
|
+
* the first invocation of this CLI (`npx ai-data-scientist setup`, or any
|
|
13
|
+
* other subcommand), and is cached via a marker file so later invocations
|
|
14
|
+
* are fast.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const { spawnSync } = require("node:child_process");
|
|
18
|
+
const crypto = require("node:crypto");
|
|
19
|
+
const fs = require("node:fs");
|
|
20
|
+
const path = require("node:path");
|
|
21
|
+
|
|
22
|
+
const PACKAGE_ROOT = path.resolve(__dirname, "..");
|
|
23
|
+
const VENV_DIR = path.join(PACKAGE_ROOT, ".venv");
|
|
24
|
+
const IS_WINDOWS = process.platform === "win32";
|
|
25
|
+
const VENV_PYTHON = path.join(
|
|
26
|
+
VENV_DIR,
|
|
27
|
+
IS_WINDOWS ? "Scripts" : "bin",
|
|
28
|
+
IS_WINDOWS ? "python.exe" : "python"
|
|
29
|
+
);
|
|
30
|
+
const PYPROJECT_PATH = path.join(PACKAGE_ROOT, "pyproject.toml");
|
|
31
|
+
const MARKER_PATH = path.join(VENV_DIR, ".ai-data-scientist-setup-complete.json");
|
|
32
|
+
|
|
33
|
+
function log(message) {
|
|
34
|
+
process.stderr.write(`[ai-data-scientist] ${message}\n`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function fileHash(filePath) {
|
|
38
|
+
return crypto.createHash("sha256").update(fs.readFileSync(filePath)).digest("hex");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function findPythonLauncher() {
|
|
42
|
+
const candidates = IS_WINDOWS ? ["python", "python3"] : ["python3", "python"];
|
|
43
|
+
for (const candidate of candidates) {
|
|
44
|
+
const probe = spawnSync(candidate, ["--version"], { stdio: "ignore" });
|
|
45
|
+
if (probe.status === 0) return candidate;
|
|
46
|
+
}
|
|
47
|
+
throw new Error(
|
|
48
|
+
"No Python 3 interpreter found on PATH (PATHにPython 3が見つかりません). " +
|
|
49
|
+
"Install Python 3.10+ and re-run this command."
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function run(command, args, options = {}) {
|
|
54
|
+
const result = spawnSync(command, args, { stdio: "inherit", cwd: PACKAGE_ROOT, ...options });
|
|
55
|
+
if (result.error) throw result.error;
|
|
56
|
+
if (result.status !== 0) {
|
|
57
|
+
throw new Error(`Command failed (${result.status}): ${command} ${args.join(" ")}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function readMarker() {
|
|
62
|
+
try {
|
|
63
|
+
return JSON.parse(fs.readFileSync(MARKER_PATH, "utf8"));
|
|
64
|
+
} catch {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function isSetupCurrent() {
|
|
70
|
+
if (!fs.existsSync(VENV_PYTHON)) return false;
|
|
71
|
+
const marker = readMarker();
|
|
72
|
+
if (!marker) return false;
|
|
73
|
+
return marker.pyprojectSha256 === fileHash(PYPROJECT_PATH);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function ensureSetup() {
|
|
77
|
+
if (isSetupCurrent()) return;
|
|
78
|
+
|
|
79
|
+
log("Setting up Python environment (初回セットアップ: Python仮想環境を構築します)...");
|
|
80
|
+
const python = findPythonLauncher();
|
|
81
|
+
|
|
82
|
+
if (!fs.existsSync(VENV_DIR)) {
|
|
83
|
+
run(python, ["-m", "venv", VENV_DIR]);
|
|
84
|
+
}
|
|
85
|
+
run(VENV_PYTHON, ["-m", "pip", "install", "--upgrade", "pip"]);
|
|
86
|
+
run(VENV_PYTHON, ["-m", "pip", "install", "-e", PACKAGE_ROOT]);
|
|
87
|
+
|
|
88
|
+
fs.writeFileSync(
|
|
89
|
+
MARKER_PATH,
|
|
90
|
+
JSON.stringify(
|
|
91
|
+
{ pyprojectSha256: fileHash(PYPROJECT_PATH), completedAt: new Date().toISOString() },
|
|
92
|
+
null,
|
|
93
|
+
2
|
|
94
|
+
)
|
|
95
|
+
);
|
|
96
|
+
log("Setup complete (セットアップが完了しました).");
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function main() {
|
|
100
|
+
const [command, ...rest] = process.argv.slice(2);
|
|
101
|
+
|
|
102
|
+
ensureSetup();
|
|
103
|
+
|
|
104
|
+
if (!command || command === "setup") {
|
|
105
|
+
log("Environment ready (環境は準備済みです).");
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (command === "test") {
|
|
110
|
+
run(VENV_PYTHON, ["-m", "pytest", ...rest]);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Pass through to the Python CLI for every other subcommand.
|
|
115
|
+
run(VENV_PYTHON, ["-m", "ai_data_scientist.cli", command, ...rest]);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
try {
|
|
119
|
+
main();
|
|
120
|
+
} catch (err) {
|
|
121
|
+
log(err.message || String(err));
|
|
122
|
+
process.exit(1);
|
|
123
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "jupytermind",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "GitHub Copilot Agent Skill: AI Data Scientist over Jupyter MCP (npm bootstrap for the Python implementation)",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"bin": {
|
|
7
|
+
"ai-data-scientist": "bin/ai-data-scientist.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin",
|
|
11
|
+
"src/ai_data_scientist/**/*.py",
|
|
12
|
+
"src/ai_chemistry_scientist/**/*.py",
|
|
13
|
+
"src/ai_genomics_scientist/**/*.py",
|
|
14
|
+
"src/ai_materials_scientist/**/*.py",
|
|
15
|
+
"src/ai_structural_biology_scientist/**/*.py",
|
|
16
|
+
"src/ai_scientist/**/*.py",
|
|
17
|
+
"src/ai_chemistry_scientist/data",
|
|
18
|
+
".github/skills/ai-data-scientist",
|
|
19
|
+
".github/skills/ai-chemistry-scientist",
|
|
20
|
+
".github/skills/ai-genomics-scientist",
|
|
21
|
+
".github/skills/ai-materials-scientist",
|
|
22
|
+
".github/skills/ai-structural-biology-scientist",
|
|
23
|
+
".github/skills/ai-scientist",
|
|
24
|
+
".github/skills/tech-writer",
|
|
25
|
+
".github/skills/japanese-prose",
|
|
26
|
+
".github/skills/presentation-planner",
|
|
27
|
+
"pyproject.toml"
|
|
28
|
+
],
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=18"
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"setup": "node bin/ai-data-scientist.js setup",
|
|
34
|
+
"test": "node bin/ai-data-scientist.js test"
|
|
35
|
+
},
|
|
36
|
+
"os": [
|
|
37
|
+
"linux",
|
|
38
|
+
"darwin",
|
|
39
|
+
"win32"
|
|
40
|
+
]
|
|
41
|
+
}
|