cyber-aced 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/.claude-plugin/plugin.json +18 -0
  2. package/.plugin/pins.json +1 -0
  3. package/.plugin/plugin.json +18 -0
  4. package/agents/aced-case-judge.md +165 -0
  5. package/agents/aced-impl-judge.md +55 -0
  6. package/agents/aced-scenario-writer.md +76 -0
  7. package/agents/aced-spec-validator.md +66 -0
  8. package/governances/README.md +52 -0
  9. package/governances/agent-tool-output.md +86 -0
  10. package/governances/cli-resolution.md +102 -0
  11. package/governances/skill-design.md +258 -0
  12. package/governances/skill-repo-structure.md +173 -0
  13. package/package.json +44 -0
  14. package/readme.md +124 -0
  15. package/skills/aced-architect-impl/README.md +9 -0
  16. package/skills/aced-architect-impl/SKILL.md +73 -0
  17. package/skills/aced-builder-impl/README.md +14 -0
  18. package/skills/aced-builder-impl/SKILL.md +46 -0
  19. package/skills/aced-builder-spec/README.md +15 -0
  20. package/skills/aced-builder-spec/SKILL.md +130 -0
  21. package/skills/aced-fit/README.md +21 -0
  22. package/skills/aced-fit/SKILL.md +105 -0
  23. package/skills/aced-impl-producer/SKILL.md +75 -0
  24. package/skills/add-scenario/README.md +25 -0
  25. package/skills/add-scenario/SKILL.md +75 -0
  26. package/skills/bench/README.md +64 -0
  27. package/skills/bench/SKILL.md +199 -0
  28. package/skills/bench/scripts/bench-claude-code.mts +187 -0
  29. package/skills/bench/scripts/bench-compare.mts +369 -0
  30. package/skills/bench/scripts/bench-record.mts +133 -0
  31. package/skills/bench/scripts/bench.mts +930 -0
  32. package/skills/check-freshness/README.md +57 -0
  33. package/skills/check-freshness/SKILL.md +76 -0
  34. package/skills/check-freshness/scripts/check-freshness.mts +399 -0
  35. package/skills/compare/README.md +25 -0
  36. package/skills/compare/SKILL.md +87 -0
  37. package/skills/contribute-skill/README.md +25 -0
  38. package/skills/contribute-skill/SKILL.md +241 -0
  39. package/skills/contribute-skill/scripts/source.mts +188 -0
  40. package/skills/define-agent/README.md +40 -0
  41. package/skills/define-agent/SKILL.md +279 -0
  42. package/skills/define-command/README.md +36 -0
  43. package/skills/define-command/SKILL.md +174 -0
  44. package/skills/define-governance/SKILL.md +151 -0
  45. package/skills/define-skill/README.md +37 -0
  46. package/skills/define-skill/SKILL.md +200 -0
  47. package/skills/extract-situation/README.md +32 -0
  48. package/skills/extract-situation/SKILL.md +54 -0
  49. package/skills/extract-situation/scripts/extract-situation.mts +287 -0
  50. package/skills/improve/README.md +25 -0
  51. package/skills/improve/SKILL.md +100 -0
  52. package/skills/improve-skill/README.md +38 -0
  53. package/skills/improve-skill/SKILL.md +169 -0
  54. package/skills/improve-skill/references/check-definitions.md +215 -0
  55. package/skills/improve-skill/scripts/validate.mts +1033 -0
  56. package/skills/init-aced/README.md +5 -0
  57. package/skills/init-aced/SKILL.md +76 -0
  58. package/skills/init-aced/scripts/ensure-results-ignored.mts +117 -0
  59. package/skills/list-skills/README.md +19 -0
  60. package/skills/list-skills/SKILL.md +55 -0
  61. package/skills/list-skills/scripts/list-skills.mts +210 -0
  62. package/skills/manage/README.md +35 -0
  63. package/skills/manage/SKILL.md +65 -0
  64. package/skills/manage-model-runners/README.md +25 -0
  65. package/skills/manage-model-runners/SKILL.md +113 -0
  66. package/skills/manage-skill-dirs/README.md +17 -0
  67. package/skills/manage-skill-dirs/SKILL.md +54 -0
  68. package/skills/manage-skill-dirs/scripts/manage-skill-dirs.mts +328 -0
  69. package/skills/repair-private-skills/README.md +17 -0
  70. package/skills/repair-private-skills/SKILL.md +59 -0
  71. package/skills/repair-private-skills/scripts/repair-private-skills.mts +279 -0
  72. package/skills/report/README.md +25 -0
  73. package/skills/report/SKILL.md +74 -0
  74. package/skills/run/README.md +25 -0
  75. package/skills/run/SKILL.md +220 -0
  76. package/skills/skillify/README.md +25 -0
  77. package/skills/skillify/SKILL.md +141 -0
@@ -0,0 +1,241 @@
1
+ ---
2
+ name: contribute-skill
3
+ description: Use this skill when contributing local improvements to an installed skill back to its source repo via PR.
4
+ ---
5
+
6
+ # Contribute Skill
7
+
8
+ When you have improved a skill you installed from another repo, this skill guides you through contributing that improvement back to the source via a pull request.
9
+
10
+ ## When to use
11
+
12
+ - You modified a skill (global `~/.agents/skills/<name>/` or repo-internal `.agents/skills/<name>/`) and want to send the improvement upstream
13
+ - The skill was installed from a public repo tracked in `skills-lock.json`
14
+
15
+ Do NOT use for repo-native skills under this repo's `skills/<name>/` — if you're the author here, this repo IS the source.
16
+
17
+ ## Source repository scope (required)
18
+
19
+ Only update files under the source repo's canonical skills tree:
20
+
21
+ ```text
22
+ <repo-root>/skills/<skill-name>/
23
+ SKILL.md
24
+ scripts/ ← include when changed
25
+ … ← any other files in that skill folder
26
+ ```
27
+
28
+ Rules:
29
+
30
+ - **Always** map upstream paths to `skills/<skill-name>/…`, even when `skills-lock.json` `skillPath` points at `.agents/skills/…` or another layout in the consumer repo.
31
+ - **Never** update `.agents/skills/`, duplicate trees, or paths outside `skills/<skill-name>/` in the source repository.
32
+ - Include every changed file under that skill folder (e.g. `scripts/*.mjs`), not only `SKILL.md`.
33
+ - Never include `SKILL.local.md` — machine-local augmentations stay local.
34
+
35
+ Derive paths:
36
+
37
+ | Input | Upstream path |
38
+ | --- | --- |
39
+ | Lock key / folder name `skillify` | `skills/<skill-name>/` |
40
+ | `skillPath`: `.agents/skills/skillify/SKILL.md` | `skills/<skill-name>/SKILL.md` |
41
+ | `skillPath`: `skills/fix-security-pr/SKILL.md` | `skills/fix-security-pr/SKILL.md` |
42
+
43
+ ## Steps
44
+
45
+ ### 1. Identify the skill(s) to patch
46
+
47
+ From context or ask the user. Look up origin using this skill's own `source.mts` script, which resolves in a fixed order: the repo-local lock, then the global lock, then `npx skills find`, returning the FIRST hit:
48
+
49
+ ```bash
50
+ node "<this-skill's-dir>/scripts/source.mts" <skill-name> --format json
51
+ ```
52
+
53
+ Output is JSON: `{ name, source, sourceUrl, skillPath, foundIn }`. Extract `source` (`owner/repo`). `foundIn` tells you which rung resolved it (`repo`, `global`, or `npx-skills`).
54
+
55
+ If the script exits non-zero (`foundIn: null`), fall back to reading the lockfiles by hand, in the same order:
56
+
57
+ ```bash
58
+ # 1. Repo-local (committed, version 1 schema):
59
+ jq '.skills["<skill-name>"]' skills-lock.json
60
+
61
+ # 2. Global (version 3 schema, has sourceUrl):
62
+ jq '.skills["<skill-name>"]' ~/.agents/.skill-lock.json
63
+
64
+ # 3. npx skills find <skill-name>
65
+ ```
66
+
67
+ If none of the three resolve, ask the user for `owner/repo` and the skill folder name — never guess.
68
+
69
+ For multiple related skills (user confirms one PR), repeat file discovery for each `skills/<name>/` directory.
70
+
71
+ ### 2. Find the local skill directory
72
+
73
+ | Kind | Local directory |
74
+ | --- | --- |
75
+ | Global | `~/.agents/skills/<name>/` |
76
+ | Repo internal | `.agents/skills/<name>/` |
77
+
78
+ Collect files to contribute (exclude machine-local files):
79
+
80
+ ```bash
81
+ find "<local-dir>" -type f ! -name 'SKILL.local.md' | sort
82
+ ```
83
+
84
+ ### 3. Diff against source
85
+
86
+ For each local file, map to `skills/<skill-name>/<relative-path>` and compare to upstream on the default branch.
87
+
88
+ List upstream skill folder (optional sanity check):
89
+
90
+ ```bash
91
+ gh api "repos/<owner>/<repo>/contents/skills/<skill-name>?ref=<default-branch>" \
92
+ --jq '.[] | .path'
93
+ # If scripts/ exists, list nested files too:
94
+ gh api "repos/<owner>/<repo>/contents/skills/<skill-name>/scripts?ref=<default-branch>" \
95
+ --jq '.[]? | .path' 2>/dev/null
96
+ ```
97
+
98
+ Per-file diff:
99
+
100
+ ```bash
101
+ UPSTREAM="skills/<skill-name>/SKILL.md"
102
+ gh api "repos/<owner>/<repo>/contents/${UPSTREAM}" --jq '.content' | base64 -d > /tmp/upstream.md
103
+ diff /tmp/upstream.md "<local-dir>/SKILL.md"
104
+ ```
105
+
106
+ - **No diffs** across all mapped files → nothing to contribute; stop
107
+ - **Any diff** → show unified diffs and get user confirmation before proceeding
108
+
109
+ ### 4. Check write access
110
+
111
+ ```bash
112
+ gh api repos/<owner>/<repo> --jq '{push: .permissions.push, default: .default_branch}'
113
+ ```
114
+
115
+ - `push: true` → branch on `owner/repo`
116
+ - `push: false` → `gh repo fork <owner>/<repo> --clone=false`, then use `<your-username>/<repo>`
117
+
118
+ ### 5. Create branch and push (single commit — Git Data API)
119
+
120
+ Use the **Git Data API** so all files land in **one commit**. Do not use `PUT /contents/{path}` per file (that creates one commit per file).
121
+
122
+ Set variables:
123
+
124
+ ```bash
125
+ OWNER=<owner>
126
+ REPO=<repo>
127
+ REPO_FULL="${OWNER}/${REPO}"
128
+ DEFAULT_BRANCH=main # from step 4
129
+ BRANCH="patch/<skill-name>-<short-slug>" # or shared slug for multi-skill PR
130
+ MSG="fix(<skill-name>): <description>"
131
+ SKILL_NAME=<skill-name>
132
+ LOCAL_DIR="<absolute-local-skill-dir>"
133
+ ```
134
+
135
+ #### 5a. Create branch ref from default branch tip
136
+
137
+ ```bash
138
+ BASE_SHA=$(gh api "repos/${REPO_FULL}/git/refs/heads/${DEFAULT_BRANCH}" --jq '.object.sha')
139
+ BASE_TREE=$(gh api "repos/${REPO_FULL}/git/commits/${BASE_SHA}" --jq '.tree.sha')
140
+
141
+ gh api "repos/${REPO_FULL}/git/refs" \
142
+ --method POST \
143
+ --field ref="refs/heads/${BRANCH}" \
144
+ --field sha="${BASE_SHA}"
145
+ ```
146
+
147
+ If the branch already exists, skip creation and set `BASE_SHA` to the branch tip instead (for amending, prefer a fresh branch).
148
+
149
+ #### 5b. Create blobs and tree entries for each local file
150
+
151
+ Build a JSON array of tree items. Each local file maps to `skills/${SKILL_NAME}/<relative-path>`.
152
+
153
+ **Use temp files for `--input`**, not inline `$(jq …)`. Embedding file content in the shell argument hits `ARG_MAX` and fails with errors like `file name too long` on typical `SKILL.md` sizes.
154
+
155
+ ```bash
156
+ TREE_ITEMS='[]'
157
+ while IFS= read -r -d '' file; do
158
+ rel="${file#${LOCAL_DIR}/}"
159
+ upstream_path="skills/${SKILL_NAME}/${rel}"
160
+
161
+ jq -n --rawfile content "$file" '{content: $content, encoding: "utf-8"}' > /tmp/patch-blob-input.json
162
+ blob_sha=$(gh api "repos/${REPO_FULL}/git/blobs" \
163
+ --method POST \
164
+ --input /tmp/patch-blob-input.json \
165
+ --jq '.sha')
166
+
167
+ TREE_ITEMS=$(jq -n \
168
+ --argjson items "$TREE_ITEMS" \
169
+ --arg path "$upstream_path" \
170
+ --arg sha "$blob_sha" \
171
+ '$items + [{path: $path, mode: "100644", type: "blob", sha: $sha}]')
172
+ done < <(find "$LOCAL_DIR" -type f ! -name 'SKILL.local.md' -print0)
173
+ ```
174
+
175
+ Repeat the loop for additional skills in the same PR (append to `TREE_ITEMS` with each skill's `SKILL_NAME` and `LOCAL_DIR`).
176
+
177
+ #### 5c. Create tree, commit, update branch ref
178
+
179
+ Write tree and commit payloads to temp files as well (large multi-skill `TREE_ITEMS` can also exceed shell limits):
180
+
181
+ ```bash
182
+ jq -n \
183
+ --arg base "$BASE_TREE" \
184
+ --argjson tree "$TREE_ITEMS" \
185
+ '{base_tree: $base, tree: $tree}' > /tmp/patch-tree-input.json
186
+ NEW_TREE=$(gh api "repos/${REPO_FULL}/git/trees" \
187
+ --method POST \
188
+ --input /tmp/patch-tree-input.json \
189
+ --jq '.sha')
190
+
191
+ jq -n \
192
+ --arg msg "$MSG" \
193
+ --arg tree "$NEW_TREE" \
194
+ --arg parent "$BASE_SHA" \
195
+ '{message: $msg, tree: $tree, parents: [$parent]}' > /tmp/patch-commit-input.json
196
+ COMMIT_SHA=$(gh api "repos/${REPO_FULL}/git/commits" \
197
+ --method POST \
198
+ --input /tmp/patch-commit-input.json \
199
+ --jq '.sha')
200
+
201
+ gh api "repos/${REPO_FULL}/git/refs/heads/${BRANCH}" \
202
+ --method PATCH \
203
+ --field sha="${COMMIT_SHA}"
204
+ ```
205
+
206
+ Result: exactly **one commit** on the patch branch containing all updated paths under `skills/<skill-name>/`.
207
+
208
+ ### 6. Open PR
209
+
210
+ ```bash
211
+ gh pr create \
212
+ --repo "${REPO_FULL}" \
213
+ --base "${DEFAULT_BRANCH}" \
214
+ --head "${BRANCH}" \
215
+ --title "fix(<skill-name>): <description>" \
216
+ --body "$(cat <<'EOF'
217
+ ## Summary
218
+
219
+ <what changed and why — only under `skills/<skill-name>/`>
220
+
221
+ ## Test plan
222
+
223
+ - [ ] Run improve-skill / `audit validate` against the updated skill(s)
224
+ - [ ] Confirm no files outside `skills/` were modified
225
+
226
+ 🤖 Generated with [Claude Code](https://claude.com/claude-code)
227
+ EOF
228
+ )"
229
+ ```
230
+
231
+ ### 7. Report
232
+
233
+ Output the PR URL. After merge, run `npx skills update` in the consumer repo to refresh `skills-lock.json` hashes.
234
+
235
+ ## What NOT to do
236
+
237
+ - Do not include `SKILL.local.md` content in the PR
238
+ - Do not push without showing diffs and getting user confirmation
239
+ - Do not create a PR if every mapped file is identical to upstream
240
+ - Do not update `.agents/skills/` or any path outside `skills/<skill-name>/` in the source repo
241
+ - Do not use Contents API `PUT` per file (multi-commit noise); use Git Data API (section 5)
@@ -0,0 +1,188 @@
1
+ #!/usr/bin/env node
2
+ // source — the concrete skill-source-resolution engine for contribute-skill. Given a skill name,
3
+ // answers where its upstream repo lives so the skill's "Locating the source repo" step never
4
+ // guesses: it walks a fixed precedence ladder — repo-local lock, then global lock, then
5
+ // `npx skills find` — and returns the FIRST hit; nothing downstream of a hit is consulted. When
6
+ // none of the three resolve, every field comes back null so the caller surfaces the gap to the
7
+ // user instead of fabricating an owner/repo.
8
+ //
9
+ // Resolution order (frozen, do not reorder):
10
+ // 1. <root>/skills-lock.json — repo-local lock (version 1 schema)
11
+ // 2. <homeDir>/.agents/.skill-lock.json — global lock (version 3 schema, carries sourceUrl)
12
+ // 3. `npx --yes skills find <skillName>` — shells out, parses "owner/repo@skillName" hits
13
+ // 4. not found — source/sourceUrl/skillPath/foundIn all null
14
+ //
15
+ // Pure functions are exported for node:test; running the file directly drives the CLI. No deps
16
+ // (the repo's node-≥23.6 convention). Imports node:* only.
17
+
18
+ import { spawnSync } from 'node:child_process'
19
+ import { existsSync, readFileSync, realpathSync } from 'node:fs'
20
+ import { homedir } from 'node:os'
21
+ import { join } from 'node:path'
22
+ import { pathToFileURL } from 'node:url'
23
+
24
+ export interface SkillSourceResult {
25
+ name: string
26
+ source: string | null
27
+ sourceUrl: string | null
28
+ skillPath: string | null
29
+ foundIn: 'repo' | 'global' | 'npx-skills' | null
30
+ }
31
+
32
+ export type NpxFindFn = (skillName: string) => string
33
+
34
+ // ── lockfile reads ──
35
+
36
+ export function readLock(lockPath: string): Record<string, unknown> | null {
37
+ if (!existsSync(lockPath)) return null
38
+ try {
39
+ return JSON.parse(readFileSync(lockPath, 'utf8'))
40
+ } catch {
41
+ return null
42
+ }
43
+ }
44
+
45
+ // ── npx skills find output parsing ──
46
+
47
+ // Strips ANSI color codes, then scans for "owner/repo@skillName" tokens; returns the first whose
48
+ // found name equals skillName.
49
+ export function parseNpxSkillsOutput(raw: string, skillName: string): { source: string } | null {
50
+ const ansiRe = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, 'g')
51
+ const clean = raw.replace(ansiRe, '')
52
+ const regex = /([a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+)@([a-zA-Z0-9_-]+)/g
53
+ let match: RegExpExecArray | null
54
+ while ((match = regex.exec(clean)) !== null) {
55
+ const [, ownerRepo, foundName] = match
56
+ if (foundName === skillName) return { source: ownerRepo! }
57
+ }
58
+ return null
59
+ }
60
+
61
+ // Real shellout: `npx --yes skills find <skillName>`, combined stdout+stderr, empty on any
62
+ // failure (npx missing, timeout, non-zero exit with no parseable output).
63
+ export function defaultNpxFind(skillName: string): string {
64
+ try {
65
+ const result = spawnSync('npx', ['--yes', 'skills', 'find', skillName], {
66
+ encoding: 'utf8',
67
+ timeout: 4_000,
68
+ env: { ...process.env, NO_COLOR: '1', CI: '1' },
69
+ })
70
+ return (result.stdout ?? '') + (result.stderr ?? '')
71
+ } catch {
72
+ return ''
73
+ }
74
+ }
75
+
76
+ // ── resolution ladder ──
77
+
78
+ // Resolves a skill's source repo by walking repo-local lock -> global lock -> npx skills find,
79
+ // returning the FIRST hit (nothing downstream of a hit is consulted). `homeDir` and `npxFind` are
80
+ // injectable for deterministic node:test coverage; production callers can omit both.
81
+ export function findSkillSource(
82
+ skillName: string,
83
+ root: string,
84
+ homeDir: string = homedir(),
85
+ npxFind: NpxFindFn = defaultNpxFind,
86
+ ): SkillSourceResult {
87
+ const repoLock = readLock(join(root, 'skills-lock.json'))
88
+ if (repoLock) {
89
+ const entry = (repoLock as { skills?: Record<string, Record<string, string>> }).skills?.[skillName]
90
+ if (entry?.source) {
91
+ return {
92
+ name: skillName,
93
+ source: entry.source,
94
+ sourceUrl: `https://github.com/${entry.source}`,
95
+ skillPath: entry.skillPath ?? null,
96
+ foundIn: 'repo',
97
+ }
98
+ }
99
+ }
100
+
101
+ const globalLock = readLock(join(homeDir, '.agents', '.skill-lock.json'))
102
+ if (globalLock) {
103
+ const entry = (globalLock as { skills?: Record<string, Record<string, string>> }).skills?.[skillName]
104
+ if (entry?.source) {
105
+ return {
106
+ name: skillName,
107
+ source: entry.source,
108
+ sourceUrl: entry.sourceUrl ?? `https://github.com/${entry.source}`,
109
+ skillPath: entry.skillPath ?? null,
110
+ foundIn: 'global',
111
+ }
112
+ }
113
+ }
114
+
115
+ const out = npxFind(skillName)
116
+ const parsed = parseNpxSkillsOutput(out, skillName)
117
+ if (parsed) {
118
+ return {
119
+ name: skillName,
120
+ source: parsed.source,
121
+ sourceUrl: `https://github.com/${parsed.source}`,
122
+ skillPath: `skills/${skillName}/SKILL.md`,
123
+ foundIn: 'npx-skills',
124
+ }
125
+ }
126
+
127
+ return {
128
+ name: skillName,
129
+ source: null,
130
+ sourceUrl: null,
131
+ skillPath: null,
132
+ foundIn: null,
133
+ }
134
+ }
135
+
136
+ // ── CLI ──
137
+
138
+ function flag(argv: string[], name: string): string | undefined {
139
+ const i = argv.indexOf(name)
140
+ return i === -1 ? undefined : argv[i + 1]
141
+ }
142
+
143
+ // The lone positional arg: any token that isn't a known flag or a known flag's value.
144
+ function positional(argv: string[]): string | undefined {
145
+ const consumed = new Set<number>()
146
+ for (const name of ['--root', '--format']) {
147
+ const i = argv.indexOf(name)
148
+ if (i !== -1) {
149
+ consumed.add(i)
150
+ consumed.add(i + 1)
151
+ }
152
+ }
153
+ for (let i = 0; i < argv.length; i++) {
154
+ if (!consumed.has(i)) return argv[i]
155
+ }
156
+ return undefined
157
+ }
158
+
159
+ export function main(argv: string[]): number {
160
+ const root = flag(argv, '--root') ?? '.'
161
+ const format = flag(argv, '--format') ?? 'text'
162
+ const skillName = positional(argv)
163
+
164
+ if (!skillName) {
165
+ process.stdout.write('usage: source <skill-name> [--root <path>] [--format json|text]\n')
166
+ return 1
167
+ }
168
+
169
+ const result = findSkillSource(skillName, root)
170
+
171
+ if (format === 'json') {
172
+ process.stdout.write(`${JSON.stringify(result)}\n`)
173
+ } else {
174
+ if (result.foundIn) {
175
+ process.stdout.write(`${result.name}: ${result.source} (found in ${result.foundIn})\n`)
176
+ if (result.skillPath) process.stdout.write(` skillPath: ${result.skillPath}\n`)
177
+ process.stdout.write(` sourceUrl: ${result.sourceUrl}\n`)
178
+ } else {
179
+ process.stdout.write(`${result.name}: not found in repo lock, global lock, or npx skills find\n`)
180
+ }
181
+ }
182
+
183
+ return result.foundIn ? 0 : 1
184
+ }
185
+
186
+ if (process.argv[1] && import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href) {
187
+ process.exit(main(process.argv.slice(2)))
188
+ }
@@ -0,0 +1,40 @@
1
+ # define-agent
2
+
3
+ Create or improve an agent definition — a named, reusable role that can be delegated as a subagent, loaded in-context as a persona, or both.
4
+
5
+ ## When to use
6
+
7
+ Use this skill when you want to author a new agent definition or improve an existing one.
8
+
9
+ Good triggers include:
10
+
11
+ - Building a conductor, orchestrator, or persona for a workflow
12
+ - Adding a reusable role to a plugin or project
13
+ - Deciding between a delegated worker and an interactive in-context persona
14
+ - Scaffolding the canonical file + runtime symlinks + companion command in one pass
15
+
16
+ ## What it does
17
+
18
+ The skill walks you through three decisions before writing anything:
19
+
20
+ 1. **Mode** — Delegated (subagent only), Invokable (dual-mode: subagent + in-context via a companion command), or In-context only
21
+ 2. **Placement** — user-global (`~/.agents/agents/`), project (`.agents/agents/`), or inside a plugin (`plugins/<name>/agents/`)
22
+ 3. **Runtimes** — which runtime symlinks to create (Claude Code, Cursor, Codex)
23
+
24
+ It then collects the role, responsibilities, output format, human-in-the-loop rules, and out-of-scope list — drafts the agent definition, runs quality checks (F1–F9, B1–B8), and for Invokable mode scaffolds a thin companion command file that loads the agent into the current session.
25
+
26
+ ## Dual-mode (Invokable) pattern
27
+
28
+ The Invokable mode implements the **dual-mode persona** pattern:
29
+
30
+ - The agent definition file is the single source of truth for the role
31
+ - A companion command reads that file into the current context at session start
32
+ - The same role body backs both delegation (subagent spawn) and in-context adoption (interactive steering)
33
+
34
+ This lets you hand off work to the agent autonomously, or step into its role yourself and operate at the same level.
35
+
36
+ ## Install
37
+
38
+ ```bash
39
+ npx skills add cyberuni/cyberplace --skill aced/define-agent
40
+ ```