Skills
Skills are prompt templates that extend mini-a’s behaviour. They live in ~/.openaf-mini-a/skills/ and can be invoked with /<name> or $<name>.
# Invoke a skill in interactive mode
/<skill-name> arg1 arg2
# Load extra skill directories
mini-a extraskills=/path/to/shared-skills
Skill formats (precedence order — first match wins):
| Format | Path | Notes |
|---|---|---|
SKILL.yaml |
~/.openaf-mini-a/skills/<name>/SKILL.yaml |
Self-contained: body + refs bundled in one file |
SKILL.yml |
~/.openaf-mini-a/skills/<name>/SKILL.yml |
Same as YAML |
SKILL.json |
~/.openaf-mini-a/skills/<name>/SKILL.json |
JSON equivalent |
SKILL.md |
~/.openaf-mini-a/skills/<name>/SKILL.md |
Classic folder skill |
skill.md |
~/.openaf-mini-a/skills/<name>/skill.md |
Lowercase alias |
<name>.md |
~/.openaf-mini-a/skills/<name>.md |
Single-file skill |
YAML skills bundle the prompt body and all @-referenced files into a single portable file — no supporting folder required. Print a starter template with mini-a --skills.
schema: mini-a.skill/v1
name: my-skill
summary: Short description shown by /skills
body: |
You are a specialized assistant for .
@context.md
refs:
context.md: |
Add context here.
Placeholders work in all formats: · · ·, ``, …
Inside folder skills, relative @file.md attachments resolve against the skill folder. In YAML skills, @-references resolve from the embedded refs map first, then fall back to the filesystem.
Folders ending in .disabled are ignored during discovery, which lets you keep a skill installed without exposing it in /skills.
Code Review Checklist
A structured skill that guides the model through a rigorous code review checklist covering correctness, security, and performance.
Target: {{arg1}}
Conduct a structured code review of {{arg1}} using the following checklist:
## Correctness
- [ ] Logic is accurate and matches the stated intent
- [ ] Edge cases and error conditions are handled
- [ ] No off-by-one errors or null pointer risks
## Security
- [ ] No hard-coded secrets or credentials
- [ ] Inputs are validated and sanitized
- [ ] Authentication and authorisation checks are present where needed
- [ ] No SQL/command injection risks
## Performance
- [ ] No unnecessary loops or N+1 queries
- [ ] Data structures are appropriate for the access pattern
- [ ] Expensive operations are cached where suitable
## Maintainability
- [ ] Code is readable and self-explanatory
- [ ] Functions are small and single-purpose
- [ ] Tests exist for critical paths
Report findings with file name, line number, severity, and suggested fix./code-review-checklist src/payments/
Explain Code
Explains a piece of code, algorithm, or module in plain English. Suitable for onboarding or documentation generation.
Target: {{arg1}}
Explain the code at {{arg1}} as if teaching a developer who is new to this
codebase but experienced with programming in general.
Structure your explanation as follows:
1. **One-line summary** — what this code does
2. **Context** — where it fits in the broader system
3. **Key concepts** — any algorithms, patterns, or domain terms
4. **Walk-through** — step-by-step explanation of the main logic
5. **Gotchas** — anything surprising or non-obvious/explain-code src/algo/kd-tree.js
Refactor Assistant
Suggests targeted refactoring improvements for a file or function. Preserves behaviour while improving readability and structure.
Target: {{arg1}}
Refactor {{arg1}} to improve readability, maintainability, and structure
without changing observable behaviour.
Apply these principles where applicable:
- Extract long functions into smaller, named helpers
- Replace magic numbers and strings with named constants
- Remove dead code and redundant comments
- Simplify complex conditionals
- Improve variable and function names
For each change, explain the motivation.
Present the refactored code followed by a diff summary./refactor src/legacy/parser.js
README Generator
Generates a comprehensive README.md for a project from its source files and structure.
Inspect the project in the current directory and generate a comprehensive
README.md with the following sections:
1. **Project name and one-line description**
2. **Features** — bullet list of key capabilities
3. **Prerequisites** — required tools and versions
4. **Installation** — copy-paste install commands
5. **Usage** — the most common commands with examples
6. **Configuration** — key environment variables or config files
7. **Contributing** — brief contribution guide
8. **License**
Base all content on the actual code and existing documentation.
Do not invent features that are not present./generate-readme
SQL Query Explainer
Explains and optimises a SQL query. Identifies potential performance issues and suggests improvements with an execution plan analysis.
SQL query:
{{arg1}}
1. Explain what this query does in plain English.
2. Identify potential performance issues (missing indexes, full-table scans,
N+1 subquery patterns, non-sargable predicates).
3. Suggest an optimised version with comments explaining each change.
4. List the indexes that would most improve this query's performance./sql-explain
Release Notes Generator (YAML format)
Drafts release notes from commits and a changelog. Demonstrates the self-contained SKILL.yaml format with embedded refs and placeholders.
schema: mini-a.skill/v1
name: release-notes
summary: Draft release notes from commits and changelog context
body: |
Produce release notes for version {{arg1}}.
Use the voice defined in @voice.md and the template in @template.md.
Commits to summarise:
{{args}}
meta:
tags: [docs, changelog]
refs:
voice.md: |
Keep language concise and user-focused.
Avoid internal jargon.
template.md: |
## Highlights
- …
## Bug Fixes
- …/release-notes v2.4.0
Security Audit
Comprehensive security audit covering OWASP Top-10, hardcoded secrets, and dependency risks. Uses the YAML skill format with embedded check refs and a children sub-folder for per-domain rules.
schema: mini-a.skill/v1
name: security-audit
summary: Comprehensive security audit covering OWASP Top-10, secrets, and dependency risks
body: |
Target: {{arg1}}
Perform a comprehensive security audit of {{arg1}}.
Work through each section defined in the reference files below:
@owasp.md
@secrets.md
@deps.md
For each finding report:
- **File** and **line number** (when applicable)
- **Severity**: Critical / High / Medium / Low / Info
- **Category**: e.g. Injection, Hardcoded Secret, Outdated Dep
- **Description**: what the issue is and why it matters
- **Remediation**: specific fix or mitigation
Finish with a one-paragraph executive summary of overall risk.
meta:
tags: [security, audit, owasp]
refs:
owasp.md: |
## OWASP Top-10 checks
- A01 Broken Access Control: missing auth checks, IDOR, path traversal
- A02 Cryptographic Failures: weak ciphers, missing TLS, unencrypted PII at rest
- A03 Injection: SQL, command, LDAP, XSS, template injection
- A04 Insecure Design: missing rate limiting, absent threat modelling artefacts
- A05 Security Misconfiguration: debug flags, default creds, overly broad CORS
- A06 Vulnerable Components: outdated libraries with known CVEs
- A07 Auth Failures: weak passwords, missing MFA, broken session handling
- A08 Integrity Failures: unsigned packages, untrusted deserialization
- A09 Logging Failures: no audit trail, sensitive data in logs
- A10 SSRF: user-supplied URLs fetched without validation
secrets.md: |
## Secrets and credential checks
Scan all files for:
- Hard-coded API keys, tokens, passwords, or private keys
- Patterns: `password =`, `secret =`, `api_key =`, `-----BEGIN`, base64 blobs > 40 chars
- .env files committed to the repository
- Credentials embedded in connection strings or config files
- Tokens appearing in log output or error messages
deps.md: |
## Dependency risk checks
- Parse dependency manifests (package.json, requirements.txt, go.mod, pom.xml, Gemfile)
- Flag packages with known CVEs or suspicious naming (typosquatting)
- Identify dependencies pinned to mutable references (branches, `latest`)
- Note transitive dependencies that dominate the vulnerability surface/security-audit src/
Docker Audit
Audits Dockerfiles and docker-compose files for security issues and layer inefficiencies. Uses the YAML skill format with separate refs for security rules, layer hygiene, and compose-specific checks.
schema: mini-a.skill/v1
name: docker-audit
summary: Audit Dockerfiles and docker-compose files for security and layer efficiency
body: |
Target: {{arg1}}
Audit the Docker artefacts at {{arg1}} (or the current directory if omitted).
If available, run `docker inspect` and `docker history` on built images to enrich the analysis.
Apply the checks from each section below:
@security.md
@layer-hygiene.md
@compose-rules.md
For each finding report:
- **File** and **line** (or image layer index)
- **Severity**: Critical / High / Medium / Low
- **Rule** violated
- **Remediation**: corrected snippet or recommended fix
End with a prioritised action list ordered by severity.
meta:
tags: [docker, security, devops]
refs:
security.md: |
## Dockerfile security checks
- Base image: prefer official minimal images (alpine, distroless); flag `latest` tags
- Running as root: flag missing `USER` instruction; verify non-root UID is set
- Secrets at build time: flag `ARG` or `ENV` carrying passwords/tokens; recommend BuildKit secrets
- `ADD` vs `COPY`: flag `ADD` with remote URLs (prefer `COPY` + explicit download step)
- Package managers: flag `apt-get` without `--no-install-recommends` and without cache cleanup in the same layer
- Exposed ports: flag unnecessarily broad `EXPOSE` directives
- `HEALTHCHECK`: flag images with no `HEALTHCHECK` instruction
- Read-only filesystem: note if the image can run with `--read-only` at runtime
layer-hygiene.md: |
## Layer efficiency checks
- Combine related `RUN` commands with `&&` to minimise layer count
- Flag cache-busting: `COPY . .` placed before dependency install steps
- Order instructions least-to-most volatile (deps before source code)
- Flag large layers: build tools, dev dependencies, or test fixtures left in the final image
- Multi-stage builds: flag single-stage builds that include compilers or build tools in the final image
- `.dockerignore`: warn if missing or lacking key entries (`.git`, `node_modules`, `*.log`)
compose-rules.md: |
## docker-compose checks
- Secrets: flag plain-text passwords in `environment:` blocks; recommend Docker secrets or env files
- Volumes: flag bind-mounts to sensitive host paths (`/etc`, `/var/run/docker.sock`)
- Network isolation: flag services on `host` network mode without justification
- Resource limits: flag services missing `mem_limit` / `cpus` constraints
- Restart policy: flag `restart: always` on services that should not auto-restart on failure
- Image versions: flag services using `:latest` or no tag at all
- Healthchecks: flag services with no `healthcheck:` and no `depends_on:` condition/docker-audit .
Test Generator
Generates a complete, runnable unit test file for a target source file. Auto-detects the framework (Jest, pytest, Go testing, OpenAF ow.test) and applies the matching template from embedded refs.
schema: mini-a.skill/v1
name: test-generator
summary: Generate unit tests for a target file, detecting the framework automatically
body: |
Target: {{arg1}}
Inspect {{arg1}} and generate a complete, runnable unit test file for it.
## Step 1 — Detect the language and test framework
Read the file extension and content, then check for framework config files:
- `jest.config.*` or `"jest"` in package.json → use @jest.md
- `pytest.ini`, `pyproject.toml`, or `setup.cfg` with `[tool:pytest]` → use @pytest.md
- `go.mod` present → use @go-test.md
- `autoTestAll.allJobs.yaml` present or `ow.load*` calls in source → use @openaf.md
If multiple signals conflict, prefer the one with the most config-file evidence.
## Step 2 — Identify what to test
- List all exported/public functions, methods, or classes
- For each: identify inputs, outputs, side effects, and error paths
- Prioritise: happy path, null/empty inputs, boundary values, error conditions
## Step 3 — Generate the test file
Follow the template for the detected framework exactly.
Cover at minimum: one happy-path test, one null/empty-input test, one error/exception test.
@jest.md
@pytest.md
@go-test.md
@openaf.md
## Step 4 — Output
Print the complete test file, ready to save and run.
State the suggested save path.
List any setup steps needed (install deps, seed data, required env vars).
meta:
tags: [testing, quality]
refs:
jest.md: |
## Jest (JavaScript / TypeScript)
Use when: `.js`, `.ts`, `.jsx`, or `.tsx` file with `package.json` present.
```javascript
import { myFunc } from '../src/myModule';
describe('myFunc', () => {
it('returns expected output for valid input', () => {
expect(myFunc('hello')).toBe('HELLO');
});
it('throws on null input', () => {
expect(() => myFunc(null)).toThrow();
});
it('handles empty string', () => {
expect(myFunc('')).toBe('');
});
});
```
Rules:
- One `describe` block per module; one `it` per behaviour
- Use `jest.fn()` for mocks; `beforeEach`/`afterEach` for setup/teardown
- Test observable behaviour, not implementation details
- Suggested path: `<dir>/__tests__/<name>.test.ts` or `<name>.spec.ts`
pytest.md: |
## pytest (Python)
Use when: `.py` files present.
```python
import pytest
from mymodule import my_func
def test_my_func_basic():
assert my_func("hello") == "HELLO"
def test_my_func_none_raises():
with pytest.raises(TypeError):
my_func(None)
def test_my_func_empty():
assert my_func("") == ""
@pytest.fixture
def sample_data():
return {"key": "value"}
def test_my_func_with_fixture(sample_data):
assert my_func(sample_data["key"]) == "VALUE"
```
Rules:
- One test file per module; prefix all test functions with `test_`
- Use `@pytest.fixture` for shared setup; `tmp_path` for temporary files
- Use `pytest.raises` for exception assertions
- Suggested path: `tests/test_<module>.py`
go-test.md: |
## Go testing (standard library)
Use when: `.go` files and `go.mod` present.
```go
package mypackage_test
import (
"testing"
"github.com/user/repo/mypackage"
)
func TestMyFuncBasic(t *testing.T) {
got := mypackage.MyFunc("hello")
want := "HELLO"
if got != want {
t.Errorf("MyFunc(%q) = %q, want %q", "hello", got, want)
}
}
func TestMyFunc_TableDriven(t *testing.T) {
tests := []struct {
name string
input string
want string
}{
{"basic", "hello", "HELLO"},
{"empty", "", ""},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := mypackage.MyFunc(tt.input); got != tt.want {
t.Errorf("got %q, want %q", got, tt.want)
}
})
}
}
```
Rules:
- Use external test package (`package foo_test`) to test the public API
- Prefer table-driven tests for multiple input/output cases
- Use `t.Helper()` in shared assertion helpers
- Suggested path: `<name>_test.go` alongside the source file
openaf.md: |
## OpenAF (ow.test framework)
Use when: `.js` files in an OpenAF project — `autoTestAll.allJobs.yaml` present or `ow.load*` calls in source.
### JS test file — `autoTestAll.<Area>.js`
```javascript
// Tests for <module description>
(function() {
exports.testMyFuncBasic = function() {
ow.loadFormat(); // load the OpenAF module under test
var result = myModule.myFunc("hello");
ow.test.assert(result, "HELLO", "myFunc should uppercase the input");
};
exports.testMyFuncNull = function() {
var result = myModule.myFunc(null);
ow.test.assert(result, null, "myFunc should return null for null input");
};
exports.testMyFuncWithCleanup = function() {
var tmpFile = "autoTestAll.myFunc.tmp";
try {
myModule.myFuncWriteFile(tmpFile, "data");
ow.test.assert(io.fileExists(tmpFile), true, "Output file should exist");
} finally {
io.rm(tmpFile);
}
};
})();
```
### Orchestrator YAML — `autoTestAll.<Area>.yaml`
```yaml
include:
- oJobTest.yaml
jobs:
- name: <Area>::Init
exec: |
args.tests = require("autoTestAll.<Area>.js");
- name: <Area>::Basic
from: <Area>::Init
to : oJob Test
exec: args.func = args.tests.testMyFuncBasic;
- name: <Area>::Null input
from: <Area>::Init
to : oJob Test
exec: args.func = args.tests.testMyFuncNull;
- name: <Area>::Cleanup
from: <Area>::Init
to : oJob Test
exec: args.func = args.tests.testMyFuncWithCleanup;
todo:
- <Area>::Init
- <Area>::Basic
- <Area>::Null input
- <Area>::Cleanup
```
Rules:
- Wrap all code in `(function() { ... })()` — no global variable leaks
- Export names must start with `test` (CamelCase after the prefix)
- Do NOT call `ow.loadTest()` — the framework loads it once before your tests run
- Load OpenAF modules (`ow.loadFormat()`, `ow.loadObj()`) inside each test function
- Clean up temporary files in a `try/finally` block using `io.rm()`
- Register the new YAML in `autoTestAll.allJobs.yaml`
- Run one area: `ojob autoTestAll.<Area>.yaml`
- Run full suite: `ojob autoTestAll.yaml`/test-generator src/utils/parser.js
oJob Builder
Generates a complete, runnable OpenAF oJob YAML file from a free-form description. Covers basic JS jobs, shell jobs, built-in shortcuts, multi-file includes, oJob-common modules, and validation patterns.
schema: mini-a.skill/v1
name: build-ojob
summary: Generate a complete OpenAF oJob YAML file from a free-form description
body: |
Build an OpenAF oJob YAML file for the following:
{{args}}
## Step 1 — Analyse the request
Identify:
- What each job does and what language it needs (default JS, shell, python, etc.)
- Whether jobs run sequentially or can run in parallel / fan-out
- Which args the user must supply (mandatory) vs. can omit (optional with defaults)
- Whether the task needs conditional branching, looping, or parallel collection processing
- Whether any external includes, remote ojob.io jobs, or oJob-common modules are a natural fit
## Step 2 — Select building blocks
Always consult @basics.md for the core structure.
Then consult whichever additional refs apply:
- @validation.md — any job that accepts typed inputs or produces typed outputs
- @shell.md — any job that runs shell/bash commands
- @shortcuts.md — conditional branching, parallel execution, or inline todo operations
- @includes.md — when the oJob uses other files, oPacks, or remote ojob.io jobs
- @common.md — when oJob-common modules (testing, SQL, HTTP, file I/O) are a natural fit
## Step 3 — Design the job graph
Before writing YAML, reason through:
- Job names — use Namespace::JobName for multi-file oJobs, plain names for single-file ones
- Execution order in todo
- Arg flow: which job sets which key, which job reads it
- from/to vs deps — use from/to for before/after wrappers; deps for strict prerequisites
- Where each(data) fan-out applies for parallel collection processing
## Step 4 — Write the oJob YAML
Produce a complete, runnable file:
- help section: text plus an expects entry for every user-facing argument
- init section: sensible defaults for optional args (available as args.init.* in all jobs)
- ojob section: opacks list, logToConsole, catch handler, any owraps/loadLibs
- jobs array: all job definitions with exec, check, catch, typeArgs as needed
- todo array: correct execution order with shortcut entries where appropriate
## Step 5 — Output
Print the complete YAML file, ready to save and run.
State the suggested filename (e.g. checkS3.yaml).
List any oPacks or system tools required.
Show the exact run command: ojob myFile.yaml arg1=value1 arg2=value2
meta:
tags: [openaf, ojob, yaml, automation]
refs:
basics.md: |
## oJob basics — structure and JavaScript jobs
### Minimal skeleton
```yaml
help:
text : What this oJob does.
expects:
- name : inputFile
desc : Path to the input file
mandatory: true
example : data.json
- name : verbose
desc : Enable verbose output
mandatory: false
example : "true"
init:
outputDir: /tmp/results # available as args.init.outputDir in all jobs
ojob:
opacks :
- openaf: ">=20230601"
logToConsole: true
catch : logErr("[" + job.name + "] " + exception)
jobs:
- name: My Job
exec: | #js
log("Running: " + job.name)
var data = io.readFileJSON(args.inputFile)
args.result = data.items.length
todo:
- My Job
```
### Job with default args using ${key:-default} syntax
```yaml
jobs:
- name: Connect
args:
host: "${dbHost:-localhost}"
port: "${dbPort:-5432}"
exec: | #js
log("Connecting to " + args.host + ":" + args.port)
```
### Sequential multi-job pipeline
```yaml
jobs:
- name: Load
exec: | #js
args.data = io.readFileJSON(args.inputFile)
- name: Transform
exec: | #js
args.data = args.data.map(r => merge(r, { processed: true }))
- name: Save
exec: | #js
io.writeFileJSON(args.outputFile, args.data, "")
todo:
- Load
- Transform
- Save
```
### from / to — before/after wrappers
`from` runs prerequisite jobs before exec; the main job inherits their args output.
`to` runs follow-up jobs after exec.
```yaml
jobs:
- name: Connect DB
exec: | #js
global.db = new DB(args.url, args.user, args.pass)
- name: Disconnect DB
exec: | #js
if (global.db) global.db.close()
- name: Query
from: Connect DB
to : Disconnect DB
exec: | #js
args.rows = global.db.q("SELECT * FROM items").results
```
### Parallel fan-out with each
```yaml
jobs:
- name: Process All Files
each:
- Process One File
exec: | #js
io.listFiles(".").files.forEach(f => each(f))
- name: Process One File
exec: | #js
log("Processing " + args.canonicalPath)
```
### Error handling
```yaml
jobs:
- name: Risky Job
catch: | #js
logErr("Failed: " + exception)
return true # true = handled; false/omit = propagate
exec : | #js
// your code here
```
### Periodic (scheduled) job
```yaml
ojob:
daemon: true
jobs:
- name : Heartbeat
type : periodic
typeArgs:
cron : "0 */5 * * * *" # every 5 minutes
waitForFinish: true
exec : | #js
log("tick " + new Date())
todo:
- Heartbeat
```
### Shutdown job
```yaml
jobs:
- name: Cleanup
type: shutdown
exec: | #js
log("shutting down — releasing resources")
```
### deps — strict prerequisites
```yaml
jobs:
- name: Main Job
deps:
- Setup Job
- name : Config Job
onSuccess: | #js
log("config loaded")
onFail : | #js
logErr("config failed")
return false # stop execution
exec: | #js
log("dependencies satisfied")
```
validation.md: |
## oJob validation — check.in and check.out
The `check` section validates and coerces args before (`in`) and after (`out`) exec.
Failures throw before the job body runs, so exec can assume clean types.
### Common validator chains
| Chain | Meaning |
|-------|---------|
| `isString` | Must be a string |
| `isNumber` | Must be a number |
| `isBoolean` | Must be a boolean |
| `isMap` | Must be an object/map |
| `isArray` | Must be an array |
| `toNumber.isNumber` | Coerce from string then validate |
| `toBoolean.isBoolean` | Coerce from string then validate |
| `.default(x)` | Use x when arg is undefined |
| `.oneOf([...])` | Restrict to an allowed set of values |
| `.between(a, b)` | Number must be in range |
| `.minLength(n)` | String or array minimum length |
| `.match(/re/)` | String must match regex |
| `.hasKeys([...])` | Map must contain listed keys |
### Example
```yaml
jobs:
- name : Import Data
check:
in:
inputFile: isString
port : toNumber.isNumber.default(5432)
mode : isString.oneOf(['fast','safe']).default('safe')
tags : isArray.default([])
enabled : toBoolean.isBoolean.default(true)
timeout : isNumber.between(1000,60000).default(30000)
out:
rowCount : isNumber.default(0)
status : isString.oneOf(['ok','error'])
exec : | #js
// args.port is already a number; args.mode is guaranteed 'fast' or 'safe'
args.rowCount = 42
args.status = "ok"
```
### Validation in todo entries
```yaml
todo:
- name: Import Data
args:
inputFile: data.csv
port : "5432" # string — toNumber coerces it
mode : fast
```
shell.md: |
## Shell jobs — lang: shell
Set `lang: shell` to run the exec body as a shell script instead of JavaScript.
### Arg flow
- Input args are available as the env variable `$aInputArgs` (a JSON string).
Individual values can be referenced inside the script as `\{{argName}}` tokens,
which oJob substitutes before executing the script.
- Output: print a single JSON object to stdout on the last line; oJob merges it
back into args. All other output lines are treated as log output.
### Minimal example
```yaml
jobs:
- name: Check Disk
lang: shell
check:
in:
path: isString.default("/")
exec: | #shell
USAGE=$(df -h \{{path}} | tail -1 | awk '{print $5}')
echo "{\"diskUsage\": \"$USAGE\"}"
```
### Reading the full args map
```yaml
jobs:
- name: Run Script
lang: shell
exec: | #shell
echo "All args: $aInputArgs"
INFILE=$(echo "$aInputArgs" | python3 -c "import sys,json; print(json.load(sys.stdin)['inputFile'])")
COUNT=$(wc -l < "$INFILE")
echo "{\"lineCount\": $COUNT}"
```
### Shell with log prefix
```yaml
jobs:
- name: Install Packages
lang: shell
typeArgs:
shellPrefix: install
exec: | #shell
apt-get update
apt-get install -y curl jq
echo "{}"
```
### Multi-step shell job with error exit
```yaml
jobs:
- name: Build and Test
lang: shell
exec: | #shell
set -e
./gradlew build
./gradlew test
echo "{\"buildStatus\": \"success\"}"
```
### Mixed JS + shell in one oJob
```yaml
jobs:
- name: Prepare
exec: | #js
args.target = io.readFileString("target.txt").trim()
- name: Deploy
lang: shell
exec: | #shell
echo "Deploying to \{{target}}..."
rsync -av dist/ user@\{{target}}:/var/www/
echo "{\"deployed\": true}"
todo:
- Prepare
- Deploy
```
shortcuts.md: |
## oJob shortcuts — concise todo operations
Shortcuts appear directly in the `todo` array (or inside `((then))`/`((else))` blocks).
Single parens `(name)` is the primary arg; double parens `((arg))` are named options.
### Conditional — (if)
```yaml
todo:
- (if ): "args.env == 'prod'"
((then)):
- Deploy to Prod
((else)):
- Deploy to Staging
```
### Parallel execution — (parallel)
```yaml
todo:
- (parallel):
- Check Service A
- Check Service B
- Check Service C
```
### Pass args inline — (pass)
```yaml
todo:
- (pass):
env : prod
region: eu-west-1
- Deploy
```
### Set / get a global key — (set) / (get)
```yaml
todo:
- (set ): configKey
((data)): { host: "db.example.com", port: 5432 }
- (get ): configKey
```
### Output results — (output)
```yaml
todo:
- (output ): results
((format)): json
```
### Call a function — (fn)
```yaml
todo:
- (fn): ow.loadFormat
```
### Load a file into args — (fileget)
```yaml
todo:
- (fileget): config.yaml
((out )): config
```
### Run an external oJob — (runfile)
```yaml
todo:
- (runfile): deploy.yaml
((args )): { target: "prod" }
```
### Channel operation — (ch)
```yaml
todo:
- (ch ): results
((op)): set
((k )): { id: 1 }
((v )): { status: "done" }
```
### Repeat N times — (repeat)
```yaml
todo:
- (repeat): 3
((todo)):
- Retry Job
```
### Loop over a collection — (each)
```yaml
todo:
- (each ): items
((todo)):
- Process Item
```
### Switch on a value — (options)
```yaml
todo:
- (options): environment
((dev )):
- Dev Setup
((prod )):
- Prod Setup
```
### State guard — (state) / when
```yaml
todo:
- (state): ready
- name : Guarded Job
when : ready
```
### Wait — (wait)
```yaml
todo:
- (wait): 5000 # wait 5 seconds
```
### Print markdown — (printmd)
```yaml
todo:
- (printmd): |
# Report
Items processed: \{{count}}
```
### LLM prompt — (llm)
```yaml
todo:
- (llm ): "Summarise the following in 3 bullet points"
((inKey )): rawData
((context)): "monthly sales records"
((outPath)): summary
```
### Security secrets — (secget)
```yaml
todo:
- (secget ): "db.password"
((secRepo )): "myrepo"
((secBucket)): "app-secrets"
```
includes.md: |
## oJob includes and oPacks
### include — full oJob (jobs + todo executed)
Loads another oJob file and runs both its jobs and its todo list.
Use for composing complete sub-workflows.
```yaml
include:
- common-setup.yaml # local file
- oJob-common::oJobTest.yaml # from an installed oPack
- ojob.io/docker/_common # remote ojob.io job, fetched at runtime
```
### jobsInclude — job definitions only (todo not run)
Imports job definitions without triggering the included file's todo.
Use for reusable job libraries.
```yaml
jobsInclude:
- ojob.io/util/osArch # adds "Get current OS and Arch" job
- shared/db-jobs.yaml # local shared definitions
```
### oPacks declaration
Declare required oPacks in `ojob.opacks`; oJob installs them if missing.
```yaml
ojob:
opacks:
- openaf: ">=20230601" # minimum version constraint
- S3 # latest version
- oJob-common
```
### owraps and loadLibs
```yaml
ojob:
owraps :
- Server # loads ow.server.*
- Java # loads ow.java.*
loadLibs:
- s3.js # JS library from an installed oPack
```
### Multi-file oJob pattern
Split large oJobs across files using Namespace::JobName conventions:
```yaml
# main.yaml — entry point
include:
- jobs/setup.yaml
- jobs/process.yaml
todo:
- Setup::Init
- Process::Run
- Setup::Teardown
```
```yaml
# jobs/setup.yaml — job definitions only, no top-level todo
jobs:
- name: Setup::Init
exec: | #js
log("initialising environment")
- name: Setup::Teardown
type: shutdown
exec: | #js
log("releasing resources")
```
### Referencing remote ojob.io jobs
```yaml
include:
- ojob.io/docker/_common # provides Docker connect/disconnect jobs
jobsInclude:
- ojob.io/util/osArch # adds "Get current OS and Arch" job only
jobs:
- name: Build Image
from: Check for docker # job provided by ojob.io/docker/_common
exec: | #js
$sh("docker build -t myimage .").get(0)
```
common.md: |
## oJob-common modules
Declare `oJob-common` in `ojob.opacks`, then include the module you need.
```yaml
ojob:
opacks:
- oJob-common
```
---
### oJobBasics — general utilities
Provides simple utility jobs: start/stop/exit, sleep, stringify, set/get paths.
```yaml
include:
- oJob-common::oJobBasics.yaml
todo:
- oJob Start
- My Job
- oJob Stop
```
---
### oJobTest — unit test framework
One init job loads the JS test file; each test job chains `from: Init` → `to: oJob Test`.
```yaml
include:
- oJob-common::oJobTest.yaml # provides the "oJob Test" runner job
jobs:
- name: MyArea::Init
exec: | #js
args.tests = require("autoTestAll.MyArea.js")
- name: MyArea::Basic case
from: MyArea::Init
to : oJob Test
exec: args.func = args.tests.testBasicCase
- name: MyArea::Error path
from: MyArea::Init
to : oJob Test
exec: args.func = args.tests.testErrorPath
todo:
- MyArea::Init
- MyArea::Basic case
- MyArea::Error path
```
Companion JS file (`autoTestAll.MyArea.js`):
```javascript
(function() {
exports.testBasicCase = function() {
ow.loadFormat()
ow.test.assert(myFunc("hello"), "HELLO", "should uppercase")
}
exports.testErrorPath = function() {
var threw = false
try { myFunc(null) } catch(e) { threw = true }
ow.test.assert(threw, true, "should throw on null input")
}
})()
```
---
### oJobSQL — database operations
```yaml
include:
- oJob-common::oJobSQL.yaml
jobs:
- name: Query Orders
exec: | #js
args.rows = $job("SQL Execute Query", {
sql: "SELECT * FROM orders WHERE status = 'open'",
db : global.db
}).results
todo:
- Query Orders
```
---
### oJobHTTPd — embedded HTTP server
```yaml
include:
- oJob-common::oJobHTTPd.yaml
ojob:
daemon: true
todo:
- name: Start HTTPd
args:
port: 8080
host: "0.0.0.0"
- name : HTTPd handler
args :
uri : /api/status
handler: | #js
return { status: "ok", time: now() }
```
---
### oJobIO — file I/O utilities
```yaml
include:
- oJob-common::oJobIO.yaml
```
Provides jobs for file copy, move, delete, compress, and recursive folder operations./build-ojob a periodic job that checks an S3 bucket and logs any new files
Suggest Skills from History
Analyzes conversation history from ~/.openaf-mini-a/history/*.json to identify recurring task patterns not yet covered by existing commands or skills, then suggests concrete new skills to build.
# Suggest Skills from History
Analyze the user's conversation history to find recurring task patterns and
recommend new mini-a skills that would automate or streamline them.
## Step 1 — Inventory existing commands and skills
```bash
ls ~/.openaf-mini-a/commands/*.md 2>/dev/null | xargs -I{} basename {} .md
ls ~/.openaf-mini-a/skills/ 2>/dev/null
```
## Step 2 — Analyze history
Run a Python script to parse ~/.openaf-mini-a/history/*.json, extract the
first substantive user goal per conversation (filtering noise like "yes",
"ok", arithmetic), and print all goals.
## Step 3 — Cluster goals into themes
Group goals by topic (code-analysis, network-lookup, web-research, etc.)
and count frequency per theme.
## Step 4 — Subtract covered themes
Cross-reference with the inventory from Step 1 and mark already-covered
themes (git operations, cheatsheet, scaffolding, humanizer, …).
## Step 5 — Generate the report
Print a prioritised Markdown report of suggested skills (frequency ≥ 3),
each with: gap description, trigger phrases, example goals, and a proposed
skill description. Include an "Already Covered" summary table./suggest-skills