Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 146 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
# RSR-compliant CI/CD workflow with SHA-pinned actions

name: CI

on:
push:
branches: ["main"]
pull_request:
branches: ["main"]

permissions:
contents: read

jobs:
lint:
name: Lint & Format
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- name: Setup Deno
uses: denoland/setup-deno@5fae568d37c3b73449009674875529a984555dd1 # v2.0.2
with:
deno-version: v2.x

- name: Check formatting
run: deno fmt --check

- name: Lint
run: deno lint

- name: Type check
run: deno check **/*.ts

test:
name: Test
runs-on: ubuntu-latest
needs: lint

steps:
- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- name: Setup Deno
uses: denoland/setup-deno@5fae568d37c3b73449009674875529a984555dd1 # v2.0.2
with:
deno-version: v2.x

- name: Run unit tests
run: deno test --allow-read --allow-write tests/unit/

- name: Run E2E tests
run: deno test --allow-read --allow-write --allow-run tests/e2e/

- name: Run tests with coverage
run: |
deno test --allow-read --allow-write --coverage=coverage/ tests/
deno coverage coverage/ --lcov > coverage.lcov

- name: Upload coverage
uses: codecov/codecov-action@1e68e06f1dbfde0e4cefc87efeba9e4643565f39 # v5.1.2
with:
files: coverage.lcov
fail_ci_if_error: false

adapter-check:
name: Adapter Validation
runs-on: ubuntu-latest
needs: lint

steps:
- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- name: Setup Deno
uses: denoland/setup-deno@5fae568d37c3b73449009674875529a984555dd1 # v2.0.2
with:
deno-version: v2.x

- name: Check adapter syntax
run: |
for f in adapters/*.js; do
echo "Checking $f..."
deno check "$f" || exit 1
done

- name: Verify adapter exports
run: |
deno eval "
const adapters = ['zola', 'hakyll', 'serum'];
for (const name of adapters) {
const mod = await import('./adapters/' + name + '.js');
if (!mod.name || !mod.language || !mod.tools) {
throw new Error(name + ' missing required exports');
}
console.log('✓', name);
}
"

build:
name: Build
runs-on: ubuntu-latest
needs: [test, adapter-check]

steps:
- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- name: Setup Deno
uses: denoland/setup-deno@5fae568d37c3b73449009674875529a984555dd1 # v2.0.2
with:
deno-version: v2.x

- name: Cache dependencies
run: deno cache mod.ts

- name: Verify build
run: |
echo "Build verification complete"
deno eval "
import { VERSION, ADAPTERS } from './mod.ts';
console.log('Version:', VERSION);
console.log('Adapters:', ADAPTERS.length);
"

container:
name: Container Build
runs-on: ubuntu-latest
needs: build
if: github.event_name == 'push' && github.ref == 'refs/heads/main'

steps:
- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- name: Build container image
run: |
podman build -t odd-ssg:${{ github.sha }} .
podman tag odd-ssg:${{ github.sha }} odd-ssg:latest

- name: Test container
run: |
podman run --rm odd-ssg:latest deno eval "console.log('Container OK')"
6 changes: 6 additions & 0 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
# odd-ssg tool versions (asdf)

deno 2.1.4
nodejs 22.12.0
just 1.38.0
120 changes: 120 additions & 0 deletions AGENTIC.scm
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
;; SPDX-License-Identifier: AGPL-3.0-or-later
;; SPDX-FileCopyrightText: 2025 Jonathan D.A. Jewell
;;; AGENTIC.scm — odd-ssg
;;
;; Configuration for AI agent interactions and MCP protocol

(define-module (odd-ssg agentic)
#:export (agent-capabilities mcp-config tool-registry constraints))

;; ============================================================================
;; Agent Capabilities
;; ============================================================================

(define agent-capabilities
'((name . "odd-ssg-agent")
(version . "0.1.0")
(description . "AI agent interface for 30 static site generators")

(can-do
(("Initialize SSG projects" . "Create new sites with any supported SSG")
("Build static sites" . "Compile content to HTML/CSS/JS")
("Serve development sites" . "Start local dev servers")
("Check site integrity" . "Validate links, content, structure")
("List available SSGs" . "Query adapter capabilities")
("Configure builds" . "Set build options and parameters")))

(cannot-do
(("Modify host filesystem arbitrarily" . "Limited to project directories")
("Access network without permission" . "Explicit permission required")
("Execute arbitrary code" . "Only predefined SSG commands")
("Persist state between sessions" . "Stateless design")))

(best-for
(("Multi-SSG workflows" . "Compare or switch between generators")
("Automated site builds" . "CI/CD integration")
("Content migration" . "Move between SSG platforms")
("Accessibility compliance" . "BSL, ASL, GSL, Makaton support")))))

;; ============================================================================
;; MCP Configuration
;; ============================================================================

(define mcp-config
'((protocol-version . "2024-11-05")

(server
(name . "odd-ssg")
(command . "deno")
(args . ("run" "--allow-read" "--allow-write" "--allow-run" "noteg-mcp/server.ts")))

(capabilities
(tools . #t)
(resources . #t)
(prompts . #f)
(logging . #t))

(resources
(adapters
(uri-template . "odd-ssg://adapters/{name}")
(description . "SSG adapter information and capabilities")))

(transport
(type . "stdio")
(encoding . "utf-8"))))

;; ============================================================================
;; Tool Registry
;; ============================================================================

(define tool-registry
'((meta-tools
((name . "odd_ssg_list_adapters")
(description . "List all available SSG adapters with their status")
(parameters . ()))

((name . "odd_ssg_connect")
(description . "Connect to an SSG adapter (verify binary availability)")
(parameters
((adapter (type . "string") (required . #t) (description . "Adapter name"))))))

(adapter-tools
;; Each adapter exposes: init, build, serve, check, version
;; Tool names follow pattern: {adapter}_{action}
;; Example: zola_build, hakyll_init, serum_serve

(common-actions
((init . "Initialize a new site project")
(build . "Build/compile the site")
(serve . "Start development server")
(check . "Validate site structure")
(clean . "Remove build artifacts")
(version . "Get SSG version"))))

(tool-count . 180))) ;; 30 adapters × 6 tools average

;; ============================================================================
;; Constraints
;; ============================================================================

(define constraints
'((security
(sandbox . "Deno permission model")
(allowed-read . ("." "./content" "./templates" "./dist"))
(allowed-write . ("./dist" "./public" "./.cache"))
(allowed-run . ("zola" "hakyll" "serum" "cryogen" "...")))

(rate-limits
(requests-per-minute . 60)
(concurrent-builds . 3)
(max-output-size . "10MB"))

(timeouts
(connect . 5000) ;; ms
(build . 300000) ;; 5 minutes
(serve . -1) ;; no timeout for serve
(check . 60000)) ;; 1 minute

(content-policy
(allow . ("static-site-generation" "content-processing" "template-rendering"))
(deny . ("arbitrary-code-execution" "network-requests" "system-modification")))))
53 changes: 53 additions & 0 deletions Containerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
# SPDX-FileCopyrightText: 2025 Jonathan D.A. Jewell
# odd-ssg Container Image

# Build stage
FROM docker.io/denoland/deno:2.1.4 AS builder

WORKDIR /app

# Copy source files
COPY deno.json deno.lock ./
COPY engine/ engine/
COPY ssg/ ssg/
COPY adapters/ adapters/
COPY noteg-lang/ noteg-lang/
COPY noteg-mcp/ noteg-mcp/

# Cache dependencies
RUN deno cache mod.ts

# Type check
RUN deno check **/*.ts

# Run tests
RUN deno test --allow-read --allow-write tests/ || true

# Production stage
FROM docker.io/denoland/deno:2.1.4-distroless

LABEL org.opencontainers.image.title="odd-ssg"
LABEL org.opencontainers.image.description="Satellite SSG adapter provider with 30 MCP-compatible adapters"
LABEL org.opencontainers.image.source="https://github.com/hyperpolymath/odd-ssg"
LABEL org.opencontainers.image.licenses="AGPL-3.0-or-later"
LABEL org.opencontainers.image.vendor="hyperpolymath"

WORKDIR /app

# Copy from builder
COPY --from=builder /app /app

# Set user
USER deno

# Default command - run MCP server
ENTRYPOINT ["deno", "run", "--allow-read", "--allow-write", "--allow-run"]
CMD ["noteg-mcp/server.ts"]

# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD deno eval "console.log('healthy')" || exit 1

# Expose MCP port
EXPOSE 3000
Loading
Loading