forked from molecule-ai/molecule-core
Forked clean from public hackathon repo (Starfire-AgentTeam, BSL 1.1) with full rebrand to Molecule AI under github.com/Molecule-AI/molecule-monorepo. Brand: Starfire → Molecule AI. Slug: starfire / agent-molecule → molecule. Env vars: STARFIRE_* → MOLECULE_*. Go module: github.com/agent-molecule/platform → github.com/Molecule-AI/molecule-monorepo/platform. Python packages: starfire_plugin → molecule_plugin, starfire_agent → molecule_agent. DB: agentmolecule → molecule. History truncated; see public repo for prior commits and contributor attribution. Verified green: go test -race ./... (platform), pytest (workspace-template 1129 + sdk 132), vitest (canvas 352), build (mcp). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
151 lines
4.2 KiB
Bash
Executable File
151 lines
4.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# bundle-compile.sh — Compile workspace-configs-templates/ into .bundle.json artifacts.
|
|
#
|
|
# Usage:
|
|
# ./bundle-compile.sh # compile all templates
|
|
# ./bundle-compile.sh seo-agent # compile one template
|
|
# ./bundle-compile.sh --output-dir ./out # custom output directory
|
|
#
|
|
# Each template folder becomes a self-contained .bundle.json that can be
|
|
# imported via POST /bundles/import.
|
|
|
|
set -euo pipefail
|
|
|
|
TEMPLATES_DIR="${TEMPLATES_DIR:-workspace-configs-templates}"
|
|
OUTPUT_DIR="."
|
|
SPECIFIC_TEMPLATE=""
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--output-dir) OUTPUT_DIR="$2"; shift 2 ;;
|
|
--templates-dir) TEMPLATES_DIR="$2"; shift 2 ;;
|
|
*) SPECIFIC_TEMPLATE="$1"; shift ;;
|
|
esac
|
|
done
|
|
|
|
mkdir -p "$OUTPUT_DIR"
|
|
|
|
compile_template() {
|
|
local dir="$1"
|
|
local id
|
|
id=$(basename "$dir")
|
|
local config="$dir/config.yaml"
|
|
|
|
if [[ ! -f "$config" ]]; then
|
|
echo " SKIP $id (no config.yaml)"
|
|
return
|
|
fi
|
|
|
|
# Extract fields from config.yaml using python (portable, no yq dependency)
|
|
local bundle
|
|
bundle=$(python3 -c "
|
|
import json, yaml, os, sys
|
|
from pathlib import Path
|
|
|
|
dir = sys.argv[1]
|
|
config_path = os.path.join(dir, 'config.yaml')
|
|
|
|
with open(config_path) as f:
|
|
config = yaml.safe_load(f)
|
|
|
|
bundle = {
|
|
'schema': '1.0',
|
|
'id': os.path.basename(dir),
|
|
'name': config.get('name', ''),
|
|
'description': config.get('description', ''),
|
|
'tier': config.get('tier', 1),
|
|
'model': config.get('model', ''),
|
|
'system_prompt': '',
|
|
'skills': [],
|
|
'tools': [{'id': t, 'config': {}} for t in config.get('tools', [])],
|
|
'prompts': {},
|
|
'sub_workspaces': [],
|
|
'agent_card': None,
|
|
'author': '',
|
|
'version': config.get('version', '1.0.0'),
|
|
}
|
|
|
|
# Load prompt files or system-prompt.md
|
|
prompt_files = config.get('prompt_files', ['system-prompt.md'])
|
|
prompts = []
|
|
for pf in prompt_files:
|
|
fp = os.path.join(dir, pf)
|
|
if os.path.exists(fp):
|
|
prompts.append(Path(fp).read_text())
|
|
bundle['system_prompt'] = '\n\n'.join(prompts)
|
|
|
|
# Store config.yaml in prompts
|
|
bundle['prompts']['config.yaml'] = Path(config_path).read_text()
|
|
|
|
# Load skills
|
|
skills_dir = os.path.join(dir, 'skills')
|
|
if os.path.isdir(skills_dir):
|
|
for skill_name in sorted(os.listdir(skills_dir)):
|
|
skill_path = os.path.join(skills_dir, skill_name)
|
|
if not os.path.isdir(skill_path):
|
|
continue
|
|
skill = {
|
|
'id': skill_name,
|
|
'name': skill_name,
|
|
'description': '',
|
|
'files': {},
|
|
}
|
|
for root, _, files in os.walk(skill_path):
|
|
for fname in sorted(files):
|
|
fpath = os.path.join(root, fname)
|
|
rel = os.path.relpath(fpath, skill_path)
|
|
try:
|
|
skill['files'][rel] = Path(fpath).read_text()
|
|
except UnicodeDecodeError:
|
|
pass
|
|
# Extract description from SKILL.md frontmatter
|
|
skill_md = skill['files'].get('SKILL.md', '')
|
|
if skill_md.startswith('---'):
|
|
parts = skill_md.split('---', 2)
|
|
if len(parts) >= 3:
|
|
try:
|
|
fm = yaml.safe_load(parts[1])
|
|
skill['name'] = fm.get('name', skill_name)
|
|
skill['description'] = fm.get('description', '')
|
|
except:
|
|
pass
|
|
bundle['skills'].append(skill)
|
|
|
|
print(json.dumps(bundle, indent=2))
|
|
" "$dir")
|
|
|
|
local outfile="$OUTPUT_DIR/${id}.bundle.json"
|
|
echo "$bundle" > "$outfile"
|
|
local size
|
|
size=$(wc -c < "$outfile" | tr -d ' ')
|
|
local skill_count
|
|
skill_count=$(echo "$bundle" | python3 -c "import sys,json; print(len(json.load(sys.stdin)['skills']))")
|
|
echo " OK $id → $outfile ($size bytes, $skill_count skills)"
|
|
}
|
|
|
|
echo "=== Bundle Compiler ==="
|
|
echo "Templates: $TEMPLATES_DIR"
|
|
echo "Output: $OUTPUT_DIR"
|
|
echo ""
|
|
|
|
count=0
|
|
if [[ -n "$SPECIFIC_TEMPLATE" ]]; then
|
|
dir="$TEMPLATES_DIR/$SPECIFIC_TEMPLATE"
|
|
if [[ -d "$dir" ]]; then
|
|
compile_template "$dir"
|
|
count=1
|
|
else
|
|
echo "ERROR: Template not found: $dir"
|
|
exit 1
|
|
fi
|
|
else
|
|
for dir in "$TEMPLATES_DIR"/*/; do
|
|
[[ -d "$dir" ]] || continue
|
|
compile_template "$dir"
|
|
count=$((count + 1))
|
|
done
|
|
fi
|
|
|
|
echo ""
|
|
echo "Compiled $count templates"
|