diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..d154b0dcc --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +# Ensure shell scripts always use LF line endings (critical for Docker builds on Windows) +*.sh text eol=lf +docker-entrypoint.sh text eol=lf diff --git a/Dockerfile b/Dockerfile index c3b991a11..a48b9b3ff 100644 --- a/Dockerfile +++ b/Dockerfile @@ -52,9 +52,10 @@ RUN set -eux; \ apk add --no-cache docker-cli; \ fi; \ if [ "$ENABLE_FULL_SKILLS" = "true" ]; then \ - apk add --no-cache python3 py3-pip nodejs npm pandoc github-cli; \ + apk add --no-cache python3 py3-pip nodejs npm pandoc github-cli poppler-utils bash; \ pip3 install --no-cache-dir --break-system-packages \ - pypdf openpyxl pandas python-pptx markitdown defusedxml lxml; \ + pypdf openpyxl pandas python-pptx markitdown defusedxml lxml \ + pdfplumber pdf2image anthropic; \ npm install -g --cache /tmp/npm-cache docx pptxgenjs; \ rm -rf /tmp/npm-cache /root/.cache /var/cache/apk/*; \ else \ @@ -77,6 +78,22 @@ COPY --from=builder /out/pkg-helper /app/pkg-helper COPY --from=builder /src/migrations/ /app/migrations/ COPY --from=builder /src/skills/ /app/bundled-skills/ COPY docker-entrypoint.sh /app/docker-entrypoint.sh + +# Fix Windows git clone issues: +# 1. CRLF line endings in shell scripts (Windows git adds \r) +# 2. Broken symlinks: On Windows (core.symlinks=false), git creates text files +# or skips symlinks entirely. Skills like docx/pptx/xlsx need _shared/office +# module in their scripts/ dir (originally symlinked as scripts/office -> ../../_shared/office). +RUN set -eux; \ + sed -i 's/\r$//' /app/docker-entrypoint.sh; \ + cd /app/bundled-skills; \ + for skill in docx pptx xlsx; do \ + if [ -d "${skill}/scripts" ] && [ ! -d "${skill}/scripts/office" ]; then \ + rm -f "${skill}/scripts/office"; \ + cp -r _shared/office "${skill}/scripts/office"; \ + fi; \ + done + RUN chmod +x /app/docker-entrypoint.sh && \ chmod 755 /app/pkg-helper && chown root:root /app/pkg-helper diff --git a/internal/skills/dep_scanner.go b/internal/skills/dep_scanner.go index 780b2049c..055f0a7bc 100644 --- a/internal/skills/dep_scanner.go +++ b/internal/skills/dep_scanner.go @@ -42,6 +42,8 @@ func scanScriptsDir(scriptsDir string) *SkillManifest { binaries := make(map[string]bool) // Track subdirectory names — these are local modules and must never be reported as missing. localModules := make(map[string]bool) + // The scripts directory itself can be referenced as a module (e.g. "from scripts import utils"). + localModules[filepath.Base(scriptsDir)] = true for _, e := range entries { if e.IsDir() { @@ -53,6 +55,19 @@ func scanScriptsDir(scriptsDir string) *SkillManifest { } for _, se := range subEntries { if se.IsDir() { + // Track nested subdirs as local modules too (e.g. office/helpers, office/validators) + // so intra-package imports like "from helpers import ..." don't get falsely reported. + localModules[se.Name()] = true + // Scan files inside nested subdirs + nestedEntries, err := os.ReadDir(filepath.Join(scriptsDir, e.Name(), se.Name())) + if err != nil { + continue + } + for _, ne := range nestedEntries { + if !ne.IsDir() { + scanFile(filepath.Join(scriptsDir, e.Name(), se.Name(), ne.Name()), pyImports, nodeImports, binaries) + } + } continue } scanFile(filepath.Join(scriptsDir, e.Name(), se.Name()), pyImports, nodeImports, binaries)