chore: scaffold supervisor from project template
This commit is contained in:
65
.context/PROJECT.md
Normal file
65
.context/PROJECT.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# Project context
|
||||
|
||||
<!-- Canonical project context. Edit this, run `task context:sync`.
|
||||
Root agent context from ~/dev/.context/AGENT.md is automatically
|
||||
prepended for harnesses that don't walk the directory tree. -->
|
||||
|
||||
## Identity
|
||||
|
||||
- **Name**: supervisor
|
||||
- **Owner**: Mathias
|
||||
- **Client**: personal
|
||||
- **Repo**:
|
||||
- **Status**: active
|
||||
|
||||
## Stack
|
||||
|
||||
- **Primary language**: Go
|
||||
- **UI layer**: HTMX + Templ (when applicable)
|
||||
- **Fallback languages**: Python, TypeScript (justify in PR if used)
|
||||
- **Build**: Task (taskfile.dev), not Make
|
||||
- **Containers**: Docker (compose for dev, k3s for deploy)
|
||||
- **Target infra**: koala (GPU workloads), iguana (services), flamingo (edge)
|
||||
|
||||
## Conventions
|
||||
|
||||
### Code style
|
||||
- Go: follow `golines`, `gofumpt`, `golangci-lint` with project config
|
||||
- Tests: table-driven, in `_test.go` next to source, `testify` for assertions
|
||||
- Errors: wrap with `fmt.Errorf("operation: %w", err)`, no naked returns
|
||||
- Naming: stdlib conventions, no stuttering (`http.Client` not `http.HTTPClient`)
|
||||
|
||||
### Architecture preferences
|
||||
- Prefer standard library over frameworks (net/http over gin/echo)
|
||||
- Dependency injection via constructor functions, not containers
|
||||
- Configuration via environment variables, parsed at startup into a typed struct
|
||||
- Structured logging via `slog`
|
||||
|
||||
### Git
|
||||
- Conventional commits: `feat:`, `fix:`, `chore:`, `docs:`, `refactor:`
|
||||
- Branch naming: `feat/short-description`, `fix/short-description`
|
||||
- PRs: one concern per PR, description explains *why* not *what*
|
||||
|
||||
### Security
|
||||
- No secrets in code, ever — use env vars or SOPS-encrypted files
|
||||
- Client data never leaves local network unless explicitly cleared
|
||||
- Dependencies: audit with `govulncheck` before adding
|
||||
|
||||
## Knowledge base access
|
||||
|
||||
This project can query the shared knowledge base via MCP or HTTP:
|
||||
|
||||
- **MCP endpoint**: `mcp://localhost:3100/knowledge`
|
||||
- **HTTP fallback**: `http://localhost:3100/api/v1/search`
|
||||
- **Scoping**: queries are filtered to collection `personal` + `public`
|
||||
|
||||
## Agent instructions
|
||||
|
||||
When acting as a coding agent on this project:
|
||||
|
||||
1. Read this file and all `SKILL.md` files in `.skills/` before starting work
|
||||
2. Run `task check` before committing (lint + test + vet)
|
||||
3. If unsure about a convention, check `DECISIONS.md` or ask
|
||||
4. Never modify files outside the project root without explicit permission
|
||||
5. When adding a dependency, explain why in the commit message
|
||||
6. For client projects: never send code or context to cloud APIs — use local models via LiteLLM
|
||||
8
.context/mcp.json
Normal file
8
.context/mcp.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"mcpServers": {
|
||||
"knowledge": {
|
||||
"url": "http://localhost:3100/mcp",
|
||||
"description": "Project knowledge base — vector + graph retrieval"
|
||||
}
|
||||
}
|
||||
}
|
||||
33
.gitignore
vendored
Normal file
33
.gitignore
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
# Binaries
|
||||
bin/
|
||||
*.exe
|
||||
|
||||
# Go
|
||||
vendor/
|
||||
|
||||
# ── Generated context files (adapter outputs) ──
|
||||
# Canonical sources: .context/PROJECT.md + .skills/*/SKILL.md
|
||||
# Everything below is disposable — regenerate with: task context:sync
|
||||
AGENTS.md
|
||||
CLAUDE.md
|
||||
.cursorrules
|
||||
.aider.conventions.md
|
||||
.aider.conf.yml
|
||||
.context/system-prompt.txt
|
||||
|
||||
# ── Sensitive ──
|
||||
.env
|
||||
.env.*
|
||||
*.key
|
||||
*.pem
|
||||
secrets/
|
||||
|
||||
# IDE
|
||||
.idea/
|
||||
.vscode/
|
||||
*.swp
|
||||
*~
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
1
.skills-shared
Symbolic link
1
.skills-shared
Symbolic link
@@ -0,0 +1 @@
|
||||
/Users/mathias/dev/.skills
|
||||
42
.skills/go-patterns/SKILL.md
Normal file
42
.skills/go-patterns/SKILL.md
Normal file
@@ -0,0 +1,42 @@
|
||||
---
|
||||
name: go-patterns
|
||||
description: Go project patterns — endpoint checklist, error handling, HTMX responses, dependency policy. Use when writing Go code, adding endpoints, or reviewing Go PRs.
|
||||
---
|
||||
|
||||
# Go project patterns
|
||||
|
||||
## New endpoint checklist
|
||||
1. Define request/response types in `types.go`
|
||||
2. Write handler in `handlers.go` using `http.HandlerFunc`
|
||||
3. Add route in `routes.go`
|
||||
4. Write table-driven test in `handlers_test.go`
|
||||
5. Run `task check` before committing
|
||||
|
||||
## Error handling pattern
|
||||
```go
|
||||
if err != nil {
|
||||
return fmt.Errorf("descriptiveOperation: %w", err)
|
||||
}
|
||||
```
|
||||
Never log and return — do one or the other.
|
||||
|
||||
## HTMX response pattern
|
||||
```go
|
||||
func (h *Handler) ListItems(w http.ResponseWriter, r *http.Request) {
|
||||
items, err := h.store.List(r.Context())
|
||||
if err != nil {
|
||||
http.Error(w, "failed to list items", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if r.Header.Get("HX-Request") == "true" {
|
||||
h.templates.Render(w, "items/_list", items)
|
||||
return
|
||||
}
|
||||
h.templates.Render(w, "items/index", items)
|
||||
}
|
||||
```
|
||||
|
||||
## Dependency policy
|
||||
- Prefer stdlib: `net/http`, `encoding/json`, `database/sql`
|
||||
- Allowed without justification: `testify`, `slog`, `templ`, `sqlc`
|
||||
- Needs justification in commit message: anything else
|
||||
31
.skills/htmx-patterns/SKILL.md
Normal file
31
.skills/htmx-patterns/SKILL.md
Normal file
@@ -0,0 +1,31 @@
|
||||
---
|
||||
name: htmx-patterns
|
||||
description: HTMX conventions — default attributes, form patterns, validation errors, hypermedia-first API design. Use when writing HTMX templates or Go handlers that return HTML fragments.
|
||||
---
|
||||
|
||||
# HTMX patterns
|
||||
|
||||
## Default attributes
|
||||
Always include on interactive elements:
|
||||
- `hx-indicator` for loading states
|
||||
- `hx-swap="innerHTML"` as default (explicit over implicit)
|
||||
- `hx-target` pointing to a specific ID, never `this` in production
|
||||
|
||||
## Form pattern
|
||||
```html
|
||||
<form hx-post="/items" hx-target="#item-list" hx-swap="beforeend" hx-indicator="#spinner">
|
||||
<input type="text" name="title" required>
|
||||
<button type="submit">Add</button>
|
||||
<span id="spinner" class="htmx-indicator">...</span>
|
||||
</form>
|
||||
```
|
||||
|
||||
## Server-sent validation errors
|
||||
Return 422 with the error fragment, swap into the form's error container:
|
||||
```html
|
||||
hx-target-422="#form-errors"
|
||||
```
|
||||
|
||||
## Prefer hypermedia over JSON
|
||||
If the endpoint returns data for display, return an HTML fragment.
|
||||
Only use JSON for machine-to-machine APIs or when a non-browser client needs it.
|
||||
53
DECISIONS.md
Normal file
53
DECISIONS.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# Decisions log
|
||||
|
||||
Record *why* things are the way they are. Future-you will thank present-you.
|
||||
|
||||
---
|
||||
|
||||
## 2026-04-08 — AGENTS.md as cross-tool standard, not CLAUDE.md
|
||||
|
||||
**Context**: Multiple tools (Crush, Pi, Antigravity) read `AGENTS.md` natively. Claude Code reads `CLAUDE.md`. Building on `CLAUDE.md` as the primary format locks into one vendor.
|
||||
|
||||
**Decision**: Canonical source is `.context/AGENT.md` (root) and `.context/PROJECT.md` (per-project). The adapter script generates both `AGENTS.md` and `CLAUDE.md` — identical content, two filenames. Crush, Pi, and Antigravity read `AGENTS.md`; Claude Code reads `CLAUDE.md`.
|
||||
|
||||
**Consequences**: One canonical file serves five+ tools. Adding a new tool that reads `AGENTS.md` requires zero adapter work.
|
||||
|
||||
## 2026-04-08 — Agent Skills standard (SKILL.md in folders) over flat markdown
|
||||
|
||||
**Context**: Claude Code, Pi, Crush, and Antigravity all support the Agent Skills open standard: a folder containing `SKILL.md` with frontmatter (`name`, `description`). Skills are discovered on-demand — only the description enters context, full instructions load when triggered.
|
||||
|
||||
**Decision**: Skills live in `.skills/{name}/SKILL.md` at project level. This replaces the earlier `.context/skills/{name}.md` flat-file approach.
|
||||
|
||||
**Consequences**: Skills are cross-compatible without adaptation. Pi auto-discovers them from `.pi/skills/` (symlink). Crush reads them natively. Progressive disclosure keeps context window lean.
|
||||
|
||||
## 2026-04-08 — Go + HTMX as default stack
|
||||
|
||||
**Context**: Need a default that's fast to prototype, easy to deploy as a single binary, and doesn't require a Node/npm toolchain for the UI layer.
|
||||
|
||||
**Decision**: Go with HTMX + Templ for server-rendered UI. Python as fallback for ML/data tasks. TypeScript only when a project genuinely needs a rich client-side SPA.
|
||||
|
||||
**Consequences**: Simpler deployment and dependency management. Agents need Go-specific skills.
|
||||
|
||||
## 2026-04-08 — Task over Make
|
||||
|
||||
**Context**: Makefiles have arcane syntax and poor cross-platform support.
|
||||
|
||||
**Decision**: Use Taskfile (taskfile.dev) — YAML-based, cross-platform, supports task dependencies.
|
||||
|
||||
**Consequences**: One extra binary to install. All project automation in `Taskfile.yml`.
|
||||
|
||||
## 2026-04-08 — Qdrant over ChromaDB for vector store
|
||||
|
||||
**Context**: Need collection-level isolation for client separation, payload filtering, runs well in k3s.
|
||||
|
||||
**Decision**: Qdrant. Native collection isolation, rich filtering, mature gRPC API.
|
||||
|
||||
**Consequences**: More operational complexity than Chroma, but isolation is non-negotiable for client work.
|
||||
|
||||
## 2026-04-08 — Mistral Vibe gets its own adapter
|
||||
|
||||
**Context**: Vibe doesn't read `AGENTS.md` — it uses `~/.vibe/prompts/` and `~/.vibe/agents/` with TOML config.
|
||||
|
||||
**Decision**: The root context-sync generates a `mathias.md` prompt and `mathias.toml` agent config in `~/.vibe/`. This is the one tool that needs a custom adapter path.
|
||||
|
||||
**Consequences**: Run `vibe --agent mathias` to use your conventions. Other Vibe users on the machine aren't affected.
|
||||
98
README.md
Normal file
98
README.md
Normal file
@@ -0,0 +1,98 @@
|
||||
# Project template
|
||||
|
||||
Harness-agnostic project scaffold using the Agent Skills open standard.
|
||||
|
||||
## Quick start
|
||||
|
||||
```bash
|
||||
degit mathias/project-template my-new-project
|
||||
cd my-new-project
|
||||
task init
|
||||
```
|
||||
|
||||
## Structure
|
||||
|
||||
```
|
||||
.context/
|
||||
├── PROJECT.md ← Canonical project context (edit this)
|
||||
├── mcp.json ← MCP server config (generated on first sync)
|
||||
└── system-prompt.txt ← Generated: generic system prompt
|
||||
|
||||
.skills/
|
||||
├── go-patterns/
|
||||
│ └── SKILL.md ← Agent Skills standard format
|
||||
└── htmx-patterns/
|
||||
└── SKILL.md
|
||||
|
||||
scripts/
|
||||
└── context-sync.sh ← Adapter generator (finds root AGENT.md automatically)
|
||||
|
||||
Taskfile.yml ← Task runner config
|
||||
DECISIONS.md ← Why things are the way they are
|
||||
```
|
||||
|
||||
## Generated files (gitignored)
|
||||
|
||||
| File | Consumer | Notes |
|
||||
|------|----------|-------|
|
||||
| `AGENTS.md` | Crush, Pi, Antigravity | Root + project concatenated |
|
||||
| `CLAUDE.md` | Claude Code | Project-only (inherits root via tree walk) |
|
||||
| `.cursorrules` | Cursor | Root + project concatenated |
|
||||
| `.aider.conventions.md` | Aider | Root + project concatenated |
|
||||
| `.context/system-prompt.txt` | Open WebUI, Mods, generic | Root + project concatenated |
|
||||
|
||||
## How root context works
|
||||
|
||||
The script walks up from the project directory looking for `~/dev/.context/AGENT.md`.
|
||||
|
||||
- **Claude Code**: inherits natively (reads every `CLAUDE.md` up the tree) → project CLAUDE.md is project-only
|
||||
- **Everything else**: can't walk the tree → script concatenates root + project into each generated file
|
||||
|
||||
## Skills
|
||||
|
||||
Skills use the [Agent Skills open standard](https://github.com/badlogic/pi-skills). Each skill is a folder with a `SKILL.md` containing frontmatter:
|
||||
|
||||
```yaml
|
||||
---
|
||||
name: my-skill
|
||||
description: What this skill does. When to use it.
|
||||
---
|
||||
# Instructions here
|
||||
```
|
||||
|
||||
Supported natively by Claude Code, Pi, Crush, and Antigravity. No adapter needed for skills.
|
||||
|
||||
### Adding a skill
|
||||
|
||||
```bash
|
||||
mkdir .skills/my-new-skill
|
||||
# Create .skills/my-new-skill/SKILL.md with frontmatter + instructions
|
||||
```
|
||||
|
||||
### Using pi-skills (cross-compatible)
|
||||
|
||||
```bash
|
||||
# User-level (all projects)
|
||||
git clone https://github.com/badlogic/pi-skills ~/.pi/agent/skills/pi-skills
|
||||
|
||||
# Symlink for Claude Code
|
||||
ln -s ~/.pi/agent/skills/pi-skills/brave-search ~/.claude/skills/brave-search
|
||||
```
|
||||
|
||||
## Usage with specific tools
|
||||
|
||||
**Claude Code**: `task context:sync:claude` → reads `CLAUDE.md` + discovers `.skills/*/SKILL.md`
|
||||
|
||||
**Crush**: `task context:sync:agents` → reads `AGENTS.md` + discovers `.skills/*/SKILL.md`
|
||||
|
||||
**Pi**: `task context:sync:agents` → reads `AGENTS.md` + discovers `.skills/*/SKILL.md` (or symlink `.skills/` to `.pi/skills/`)
|
||||
|
||||
**Antigravity**: `task context:sync:agents` → reads `AGENTS.md` + discovers `.skills/*/SKILL.md`
|
||||
|
||||
**Cursor**: `task context:sync:cursor` → reads `.cursorrules`
|
||||
|
||||
**Mistral Vibe**: Run root-level `task context:sync:vibe` once → `vibe --agent mathias`
|
||||
|
||||
**Open WebUI / Mods**: Copy `.context/system-prompt.txt` into a preset or pipe it
|
||||
|
||||
**Any other tool**: Point at `.context/PROJECT.md` directly — it's human-readable markdown
|
||||
57
Taskfile.yml
Normal file
57
Taskfile.yml
Normal file
@@ -0,0 +1,57 @@
|
||||
version: '3'
|
||||
|
||||
vars:
|
||||
PROJECT_NAME: '{{.PROJECT_NAME | default "myproject"}}'
|
||||
|
||||
tasks:
|
||||
context:sync:
|
||||
desc: Regenerate all harness-specific context files
|
||||
cmds:
|
||||
- bash scripts/context-sync.sh
|
||||
sources:
|
||||
- .context/PROJECT.md
|
||||
- .skills/*/SKILL.md
|
||||
|
||||
context:sync:claude:
|
||||
cmds: [bash scripts/context-sync.sh claude]
|
||||
context:sync:agents:
|
||||
cmds: [bash scripts/context-sync.sh agents]
|
||||
context:sync:cursor:
|
||||
cmds: [bash scripts/context-sync.sh cursor]
|
||||
|
||||
dev:
|
||||
desc: Start development server
|
||||
cmds:
|
||||
- go run ./cmd/server
|
||||
|
||||
build:
|
||||
desc: Build the binary
|
||||
cmds:
|
||||
- go build -o bin/{{.PROJECT_NAME}} ./cmd/server
|
||||
|
||||
check:
|
||||
desc: Run all checks (lint + test + vet)
|
||||
cmds:
|
||||
- task: lint
|
||||
- task: test
|
||||
- task: vet
|
||||
|
||||
lint:
|
||||
cmds: [golangci-lint run ./...]
|
||||
test:
|
||||
cmds: [go test -race -count=1 ./...]
|
||||
vet:
|
||||
cmds:
|
||||
- go vet ./...
|
||||
- govulncheck ./... || true
|
||||
|
||||
up:
|
||||
desc: Start containers
|
||||
cmds: [docker compose up -d]
|
||||
down:
|
||||
cmds: [docker compose down]
|
||||
|
||||
init:
|
||||
desc: Initialize a new project from this template
|
||||
cmds:
|
||||
- bash scripts/init.sh
|
||||
153
scripts/context-sync.sh
Executable file
153
scripts/context-sync.sh
Executable file
@@ -0,0 +1,153 @@
|
||||
#!/usr/bin/env bash
|
||||
# Generates harness-specific context files from .context/PROJECT.md
|
||||
# Project-level script — run from a project directory.
|
||||
#
|
||||
# For Claude Code: generates project-only CLAUDE.md (it inherits root via tree walk)
|
||||
# For everything else: concatenates root AGENT.md + project PROJECT.md
|
||||
#
|
||||
# Usage: ./scripts/context-sync.sh [adapter...]
|
||||
# Task: task context:sync
|
||||
#
|
||||
# Override root context: ROOT_CONTEXT=~/dev/.context/AGENT.md ./scripts/context-sync.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
PROJECT_FILE=".context/PROJECT.md"
|
||||
SKILLS_DIR=".skills"
|
||||
|
||||
# Walk up to find root .context/AGENT.md
|
||||
find_root_context() {
|
||||
local dir
|
||||
dir="$(pwd)"
|
||||
while [ "$dir" != "/" ]; do
|
||||
dir="$(dirname "$dir")"
|
||||
if [ -f "$dir/.context/AGENT.md" ]; then
|
||||
echo "$dir/.context/AGENT.md"
|
||||
return
|
||||
fi
|
||||
done
|
||||
echo ""
|
||||
}
|
||||
|
||||
ROOT_CONTEXT="${ROOT_CONTEXT:-$(find_root_context)}"
|
||||
|
||||
if [ ! -f "$PROJECT_FILE" ]; then
|
||||
echo "Error: $PROJECT_FILE not found. Are you in a project root?"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -n "$ROOT_CONTEXT" ] && [ -f "$ROOT_CONTEXT" ]; then
|
||||
echo " Root context: $ROOT_CONTEXT"
|
||||
else
|
||||
echo " No root AGENT.md found (project context only)"
|
||||
fi
|
||||
|
||||
# Emit root context + separator
|
||||
root_block() {
|
||||
if [ -n "$ROOT_CONTEXT" ] && [ -f "$ROOT_CONTEXT" ]; then
|
||||
cat "$ROOT_CONTEXT"
|
||||
echo ""
|
||||
echo "---"
|
||||
echo ""
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Claude Code ──────────────────────────────────────────────
|
||||
# Claude Code walks up the tree — it finds ~/dev/CLAUDE.md automatically.
|
||||
# Project-level CLAUDE.md only needs project-specific context.
|
||||
generate_claude() {
|
||||
cat "$PROJECT_FILE" > CLAUDE.md
|
||||
echo " → CLAUDE.md (project-only; Claude Code inherits root)"
|
||||
}
|
||||
|
||||
# ── AGENTS.md (Crush, Pi, Antigravity) ──────────────────────
|
||||
# These tools read AGENTS.md from cwd but don't walk up.
|
||||
# Concatenate root + project.
|
||||
generate_agents() {
|
||||
{ root_block; cat "$PROJECT_FILE"; } > AGENTS.md
|
||||
echo " → AGENTS.md (root + project; Crush, Pi, Antigravity)"
|
||||
}
|
||||
|
||||
# ── Cursor ───────────────────────────────────────────────────
|
||||
generate_cursor() {
|
||||
{
|
||||
echo "# Cursor rules — auto-generated"
|
||||
echo "# Do not edit. Run: task context:sync"
|
||||
echo ""
|
||||
root_block
|
||||
cat "$PROJECT_FILE"
|
||||
} > .cursorrules
|
||||
echo " → .cursorrules (root + project)"
|
||||
}
|
||||
|
||||
# ── Aider ────────────────────────────────────────────────────
|
||||
generate_aider() {
|
||||
{ root_block; cat "$PROJECT_FILE"; } > .aider.conventions.md
|
||||
if [ ! -f .aider.conf.yml ]; then
|
||||
cat > .aider.conf.yml << 'YAML'
|
||||
read: .aider.conventions.md
|
||||
auto-commits: false
|
||||
YAML
|
||||
fi
|
||||
echo " → .aider.conventions.md (root + project)"
|
||||
}
|
||||
|
||||
# ── Generic system prompt (Open WebUI, Mods, etc.) ──────────
|
||||
generate_system_prompt() {
|
||||
{
|
||||
echo "You are a coding assistant working on a specific project."
|
||||
echo "Follow all conventions from both the root agent context and project context."
|
||||
echo ""
|
||||
echo "---"
|
||||
echo ""
|
||||
root_block
|
||||
cat "$PROJECT_FILE"
|
||||
echo ""
|
||||
echo "---"
|
||||
} > .context/system-prompt.txt
|
||||
echo " → .context/system-prompt.txt (root + project)"
|
||||
}
|
||||
|
||||
# ── MCP config ───────────────────────────────────────────────
|
||||
generate_mcp() {
|
||||
if [ ! -f .context/mcp.json ]; then
|
||||
cat > .context/mcp.json << 'JSON'
|
||||
{
|
||||
"mcpServers": {
|
||||
"knowledge": {
|
||||
"url": "http://localhost:3100/mcp",
|
||||
"description": "Project knowledge base — vector + graph retrieval"
|
||||
}
|
||||
}
|
||||
}
|
||||
JSON
|
||||
echo " → .context/mcp.json (new)"
|
||||
else
|
||||
echo " → .context/mcp.json (exists, skipped)"
|
||||
fi
|
||||
}
|
||||
|
||||
echo "Syncing project context from $PROJECT_FILE..."
|
||||
|
||||
if [ $# -eq 0 ]; then
|
||||
generate_claude
|
||||
generate_agents
|
||||
generate_cursor
|
||||
generate_aider
|
||||
generate_system_prompt
|
||||
generate_mcp
|
||||
else
|
||||
for adapter in "$@"; do
|
||||
case "$adapter" in
|
||||
claude) generate_claude ;;
|
||||
agents) generate_agents ;;
|
||||
cursor) generate_cursor ;;
|
||||
aider) generate_aider ;;
|
||||
prompt|system|openwebui|owui|generic) generate_system_prompt ;;
|
||||
mcp) generate_mcp ;;
|
||||
*) echo "Unknown adapter: $adapter" ;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
|
||||
echo "Done."
|
||||
14
scripts/init.sh
Executable file
14
scripts/init.sh
Executable file
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
echo "Project name:"
|
||||
read name
|
||||
echo "Client tag (or 'personal'):"
|
||||
read client
|
||||
|
||||
sed -i '' "s/{{PROJECT_NAME}}/$name/g" .context/PROJECT.md 2>/dev/null || sed -i "s/{{PROJECT_NAME}}/$name/g" .context/PROJECT.md
|
||||
sed -i '' "s/{{CLIENT_TAG or \"personal\"}}/$client/g" .context/PROJECT.md 2>/dev/null || sed -i "s/{{CLIENT_TAG or \"personal\"}}/$client/g" .context/PROJECT.md
|
||||
sed -i '' "s/{{CLIENT_TAG}}/$client/g" .context/PROJECT.md 2>/dev/null || sed -i "s/{{CLIENT_TAG}}/$client/g" .context/PROJECT.md
|
||||
|
||||
task context:sync
|
||||
echo "✓ Project initialized: $name (client: $client)"
|
||||
Reference in New Issue
Block a user