@taskclan/achilleon 0.3.0 → 0.5.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.
package/README.md CHANGED
@@ -10,9 +10,9 @@ Three kinds of entries live in this repo:
10
10
 
11
11
  | Kind | What it is | Where it runs |
12
12
  |---|---|---|
13
- | **Skill** | A single slash command with a fixed system prompt and tier binding | Inside `@taskclan` on the [VS Code extension](https://marketplace.visualstudio.com/items?itemName=taskclan.taskclan-intelligence) and (eventually) the CLI + web playground |
14
- | **Agent** | A named chat participant with a locked personality (`@taskclan-reviewer`, `@taskclan-guru`, ...) | VS Code chat |
15
- | **Prompt** | A parameterised prompt template loaded by key from the Taskclan SDK | Anywhere the SDK runs |
13
+ | **Skill** | A single slash command with a fixed system prompt and tier binding | Inside `@taskclan` on the [VS Code extension](https://marketplace.visualstudio.com/items?itemName=taskclan.taskclan-intelligence), Cursor / Continue / Claude Code (via `dist/*` bundles), or anywhere else you can paste a prompt |
14
+ | **Agent** | A named chat participant with a locked personality (`@taskclan-reviewer`, `@taskclan-guru`, ...) | VS Code chat via the extension; also exportable as Cursor / Continue slash commands |
15
+ | **Prompt** | A parameterised template with `{{variables}}` you fill in values at call time | Any SDK consumer via `render()` from `@taskclan/achilleon`. Ships with helpers for `summarize`, `translate`, `extract`, `classify`, `judge`, `refine`. |
16
16
 
17
17
  ## Directory shape
18
18
 
@@ -110,6 +110,44 @@ Skills are also emitted as plain markdown you can paste into any system-prompt /
110
110
  # Or grab one
111
111
  curl -O https://raw.githubusercontent.com/taskclan/achilleon/main/dist/raw/debug.md
112
112
 
113
+ ### npm — for SDK / tooling authors
114
+
115
+ If you are building your own tool and want the registry as typed data:
116
+
117
+ npm install @taskclan/achilleon
118
+
119
+ ```ts
120
+ import { skills, agents, prompts, render, getSkill, byTier, categories } from '@taskclan/achilleon';
121
+
122
+ // Every skill and agent, typed.
123
+ for (const s of skills) {
124
+ console.log(s.id, s.tier, s.description);
125
+ }
126
+
127
+ // One entry by id.
128
+ const debug = getSkill('debug');
129
+ console.log(debug?.system);
130
+
131
+ // Group by tier.
132
+ const heavyReasoners = byTier('t1-max');
133
+
134
+ // Render a parameterised prompt template.
135
+ const filled = render('summarize', {
136
+ text: articleBody,
137
+ length: '5 bullets',
138
+ });
139
+ // filled is now a full prompt string — hand it to any LLM.
140
+ ```
141
+
142
+ CJS also works: `const { skills } = require('@taskclan/achilleon')`.
143
+
144
+ Raw JSON is also directly importable:
145
+
146
+ ```ts
147
+ import registry from '@taskclan/achilleon/registry';
148
+ // { skills: AchilleonSkill[], agents: AchilleonAgent[] }
149
+ ```
150
+
113
151
  ### Taskclan Intelligence VS Code extension (first-party)
114
152
 
115
153
  If you install [`taskclan.taskclan-intelligence`](https://marketplace.visualstudio.com/items?itemName=taskclan.taskclan-intelligence) from the VS Code Marketplace, you get every skill wired as `@taskclan /<name>` with T1 routing baked in. The extension pulls Achilleon at build time so new entries land in the next release automatically.
@@ -5,13 +5,15 @@ tier: t1-max
5
5
  hosts:
6
6
  - vscode
7
7
  author: taskclan
8
- version: 1.0.0
8
+ version: 1.1.0
9
9
  tags:
10
10
  - architecture
11
11
  - design
12
12
  capabilities:
13
13
  - tool_use
14
14
  - extended_thinking
15
+ - vision
16
+ - documents
15
17
  system: |
16
18
  You are the Taskclan architect. The user brings a design question or
17
19
  a system diagram. Respond at the architectural altitude: tradeoffs,
@@ -5,12 +5,14 @@ tier: t1-max
5
5
  hosts:
6
6
  - vscode
7
7
  author: taskclan
8
- version: 1.0.0
8
+ version: 1.1.0
9
9
  tags:
10
10
  - security
11
11
  - review
12
12
  capabilities:
13
13
  - tool_use
14
+ - vision
15
+ - documents
14
16
  system: |
15
17
  You are the Taskclan security-first reviewer, adversarial mindset.
16
18
  Everything the user shows you gets read the way an attacker would
@@ -5,12 +5,14 @@ tier: t1-max
5
5
  hosts:
6
6
  - vscode
7
7
  author: taskclan
8
- version: 1.0.0
8
+ version: 1.1.0
9
9
  tags:
10
10
  - review
11
11
  - quality
12
12
  capabilities:
13
13
  - tool_use
14
+ - vision
15
+ - documents
14
16
  system: |
15
17
  You are the Taskclan senior reviewer. Everything the user shows you
16
18
  gets a staff-engineer code review. Flag: correctness bugs, race
package/dist/agents.json CHANGED
@@ -9,14 +9,16 @@
9
9
  "vscode"
10
10
  ],
11
11
  "author": "taskclan",
12
- "version": "1.0.0",
12
+ "version": "1.1.0",
13
13
  "tags": [
14
14
  "architecture",
15
15
  "design"
16
16
  ],
17
17
  "capabilities": [
18
18
  "tool_use",
19
- "extended_thinking"
19
+ "extended_thinking",
20
+ "vision",
21
+ "documents"
20
22
  ],
21
23
  "system": "You are the Taskclan architect. The user brings a design question or\na system diagram. Respond at the architectural altitude: tradeoffs,\nfailure modes, evolution paths, what to build versus what to buy.\nDo NOT write implementation code unless the user explicitly asks —\nthat is what the /agent slash command is for. Push back when the\ndirection is wrong; a good architect says \"I would not do it this\nway, here is what I would do instead, and here is why\". Ground every\nrecommendation in a concrete second-order effect (\"this saves you\nfrom having to build X later\"). Skip generalities; be specific to\nthe user's stack.\n"
22
24
  },
@@ -47,13 +49,15 @@
47
49
  "vscode"
48
50
  ],
49
51
  "author": "taskclan",
50
- "version": "1.0.0",
52
+ "version": "1.1.0",
51
53
  "tags": [
52
54
  "security",
53
55
  "review"
54
56
  ],
55
57
  "capabilities": [
56
- "tool_use"
58
+ "tool_use",
59
+ "vision",
60
+ "documents"
57
61
  ],
58
62
  "system": "You are the Taskclan security-first reviewer, adversarial mindset.\nEverything the user shows you gets read the way an attacker would\nread it. Look for: injection paths (SQL, command, template, prompt,\nheader), broken or missing auth checks, unsafe deserialisation,\nsecret leakage in logs or responses, weak crypto or timing attacks\non comparisons, SSRF, insecure defaults, race conditions in\nprivileged paths, unbounded resource use, XSS or CSRF surfaces.\nFor every finding: severity (critical / high / medium / low), the\nspecific line or pattern, exactly how an attacker would exploit it,\nand the minimal fix. Rank by severity, critical first. Ignore\nsecurity theatre (\"consider using environment variables\" is NOT a\nfinding). If the code is genuinely clean, say \"no findings\" and stop.\n"
59
63
  },
@@ -67,13 +71,15 @@
67
71
  "vscode"
68
72
  ],
69
73
  "author": "taskclan",
70
- "version": "1.0.0",
74
+ "version": "1.1.0",
71
75
  "tags": [
72
76
  "review",
73
77
  "quality"
74
78
  ],
75
79
  "capabilities": [
76
- "tool_use"
80
+ "tool_use",
81
+ "vision",
82
+ "documents"
77
83
  ],
78
84
  "system": "You are the Taskclan senior reviewer. Everything the user shows you\ngets a staff-engineer code review. Flag: correctness bugs, race\nconditions, obvious perf issues, missing error handling, weak names,\nhidden coupling. Be direct. Rank findings by blast radius, highest\nfirst. Do not comment on style unless it obscures meaning. If the\ncode is fine, say \"no findings\" and stop.\n"
79
85
  },
package/dist/index.cjs CHANGED
@@ -5,15 +5,35 @@ const registry = JSON.parse(readFileSync(join(__dirname, 'registry.json'), 'utf8
5
5
 
6
6
  const skills = registry.skills;
7
7
  const agents = registry.agents;
8
+ const prompts = registry.prompts;
8
9
  const entries = [...registry.agents, ...registry.skills];
9
10
 
11
+ function _render(id, values, prompts) {
12
+ const p = prompts.find((x) => x.id === id);
13
+ if (!p) throw new Error('achilleon: prompt "' + id + '" not found');
14
+ const provided = values || {};
15
+ const used = new Set();
16
+ const output = p.template.replace(/\{\{\s*([a-z][a-z_0-9]{0,39})\s*\}\}/g, (_, name) => {
17
+ used.add(name);
18
+ if (provided[name] !== undefined && provided[name] !== null) return String(provided[name]);
19
+ const varDef = (p.variables || []).find((v) => v.name === name);
20
+ if (varDef && varDef.default !== undefined) return varDef.default;
21
+ if (varDef && varDef.required === false) return '';
22
+ throw new Error('achilleon: prompt "' + id + '" missing required variable "' + name + '"');
23
+ });
24
+ return output;
25
+ }
26
+
10
27
  module.exports = {
11
28
  skills,
12
29
  agents,
30
+ prompts,
13
31
  entries,
14
32
  getSkill: (id) => skills.find((s) => s.id === id),
15
33
  getAgent: (id) => agents.find((a) => a.id === id),
16
34
  getEntry: (id) => entries.find((e) => e.id === id),
35
+ getPrompt: (id) => prompts.find((p) => p.id === id),
17
36
  categories: () => [...new Set(skills.map((s) => s.category))].sort(),
18
37
  byTier: (tier) => entries.filter((e) => e.tier === tier),
38
+ render: (id, values) => _render(id, values, prompts),
19
39
  };
package/dist/index.d.ts CHANGED
@@ -26,7 +26,13 @@ export interface AchilleonSkill {
26
26
  examples?: string[];
27
27
  }
28
28
 
29
- export type AgentCapability = 'tool_use' | 'vision' | 'extended_thinking';
29
+ export type AgentCapability =
30
+ | 'tool_use'
31
+ | 'vision'
32
+ | 'extended_thinking'
33
+ | 'video'
34
+ | 'audio'
35
+ | 'documents';
30
36
 
31
37
  export interface AchilleonAgent {
32
38
  kind: 'agent';
@@ -44,13 +50,44 @@ export interface AchilleonAgent {
44
50
 
45
51
  export type AchilleonEntry = AchilleonSkill | AchilleonAgent;
46
52
 
53
+ export interface AchilleonPromptVariable {
54
+ name: string;
55
+ description: string;
56
+ required?: boolean;
57
+ default?: string;
58
+ }
59
+
60
+ export interface AchilleonPrompt {
61
+ kind: 'prompt';
62
+ id: string;
63
+ name: string;
64
+ description: string;
65
+ template: string;
66
+ variables?: AchilleonPromptVariable[];
67
+ tier?: SkillTier;
68
+ author?: string;
69
+ version?: string;
70
+ tags?: string[];
71
+ }
72
+
47
73
  export const skills: AchilleonSkill[];
48
74
  export const agents: AchilleonAgent[];
75
+ export const prompts: AchilleonPrompt[];
49
76
  /** Agents first, then skills, in the order they were loaded. */
50
77
  export const entries: AchilleonEntry[];
51
78
 
52
79
  export function getSkill(id: string): AchilleonSkill | undefined;
53
80
  export function getAgent(id: string): AchilleonAgent | undefined;
54
81
  export function getEntry(id: string): AchilleonEntry | undefined;
82
+ export function getPrompt(id: string): AchilleonPrompt | undefined;
55
83
  export function categories(): SkillCategory[];
56
84
  export function byTier(tier: SkillTier): AchilleonEntry[];
85
+
86
+ /**
87
+ * Render a prompt template with values. Missing required variables throw;
88
+ * missing optional variables substitute the empty string.
89
+ *
90
+ * import { render } from '@taskclan/achilleon';
91
+ * const filled = render('summarize', { text: articleBody, length: '5 bullets' });
92
+ */
93
+ export function render(id: string, values: Record<string, string | number | boolean>): string;
package/dist/index.mjs CHANGED
@@ -4,11 +4,12 @@ import { dirname, join } from 'node:path';
4
4
 
5
5
  const HERE = dirname(fileURLToPath(import.meta.url));
6
6
 
7
- /** @type {{ skills: any[], agents: any[] }} */
7
+ /** @type {{ skills: any[], agents: any[], prompts: any[] }} */
8
8
  const registry = JSON.parse(readFileSync(join(HERE, 'registry.json'), 'utf8'));
9
9
 
10
10
  export const skills = registry.skills;
11
11
  export const agents = registry.agents;
12
+ export const prompts = registry.prompts;
12
13
  export const entries = [...registry.agents, ...registry.skills];
13
14
 
14
15
  export function getSkill(id) {
@@ -23,6 +24,10 @@ export function getEntry(id) {
23
24
  return entries.find((e) => e.id === id);
24
25
  }
25
26
 
27
+ export function getPrompt(id) {
28
+ return prompts.find((p) => p.id === id);
29
+ }
30
+
26
31
  /** All skill categories, sorted, unique. */
27
32
  export function categories() {
28
33
  return [...new Set(skills.map((s) => s.category))].sort();
@@ -32,3 +37,24 @@ export function categories() {
32
37
  export function byTier(tier) {
33
38
  return entries.filter((e) => e.tier === tier);
34
39
  }
40
+
41
+ function _render(id, values, prompts) {
42
+ const p = prompts.find((x) => x.id === id);
43
+ if (!p) throw new Error('achilleon: prompt "' + id + '" not found');
44
+ const provided = values || {};
45
+ const used = new Set();
46
+ const output = p.template.replace(/\{\{\s*([a-z][a-z_0-9]{0,39})\s*\}\}/g, (_, name) => {
47
+ used.add(name);
48
+ if (provided[name] !== undefined && provided[name] !== null) return String(provided[name]);
49
+ const varDef = (p.variables || []).find((v) => v.name === name);
50
+ if (varDef && varDef.default !== undefined) return varDef.default;
51
+ if (varDef && varDef.required === false) return '';
52
+ throw new Error('achilleon: prompt "' + id + '" missing required variable "' + name + '"');
53
+ });
54
+ return output;
55
+ }
56
+
57
+ /** Render a prompt template with values. Missing required vars throw. */
58
+ export function render(id, values) {
59
+ return _render(id, values, prompts);
60
+ }
@@ -0,0 +1,193 @@
1
+ [
2
+ {
3
+ "kind": "prompt",
4
+ "id": "classify",
5
+ "name": "Classify into a fixed set",
6
+ "description": "Classify a piece of text into exactly one of a small set of labels. Returns just the label.",
7
+ "tier": "t1-core",
8
+ "author": "taskclan",
9
+ "version": "1.0.0",
10
+ "tags": [
11
+ "classification"
12
+ ],
13
+ "variables": [
14
+ {
15
+ "name": "text",
16
+ "description": "The text to classify.",
17
+ "required": true
18
+ },
19
+ {
20
+ "name": "labels",
21
+ "description": "Comma-separated list of allowed labels. E.g. \"billing, technical, refund, general\".",
22
+ "required": true
23
+ },
24
+ {
25
+ "name": "unknown_label",
26
+ "description": "The label to return when no other label fits. E.g. \"other\" or \"unknown\".",
27
+ "required": false,
28
+ "default": "unknown"
29
+ }
30
+ ],
31
+ "template": "Classify the text below into EXACTLY ONE of these labels:\n{{labels}}\n\nIf none fit, respond with: {{unknown_label}}\n\nRules:\n- Output the single label, lowercase, and NOTHING else.\n- No explanation, no punctuation, no code fences.\n\n--- TEXT ---\n{{text}}\n--- END ---\n"
32
+ },
33
+ {
34
+ "kind": "prompt",
35
+ "id": "extract",
36
+ "name": "Extract structured data",
37
+ "description": "Pull structured fields out of unstructured text into a JSON object matching the shape you describe.",
38
+ "tier": "t1-flow",
39
+ "author": "taskclan",
40
+ "version": "1.0.0",
41
+ "tags": [
42
+ "extraction",
43
+ "structured"
44
+ ],
45
+ "variables": [
46
+ {
47
+ "name": "text",
48
+ "description": "Unstructured source text.",
49
+ "required": true
50
+ },
51
+ {
52
+ "name": "shape",
53
+ "description": "Description of the JSON shape you want out. For example a TypeScript-style type such as \"name: string; email: string; company: string or null\".\n",
54
+ "required": true
55
+ },
56
+ {
57
+ "name": "strict",
58
+ "description": "If \"true\", missing fields become null. If \"false\", the model may infer plausible values from context.",
59
+ "required": false,
60
+ "default": "true"
61
+ }
62
+ ],
63
+ "template": "Read the source text below and extract a JSON object matching this shape:\n\n {{shape}}\n\nRules:\n- Output ONLY the JSON object. No prose, no code fences, no commentary.\n- When strict={{strict}}: for missing fields, use null. Do not invent values.\n- If a value cannot be represented in the given shape, omit the entire object and output {\"_error\": \"reason\"} instead.\n\n--- SOURCE ---\n{{text}}\n--- END ---\n"
64
+ },
65
+ {
66
+ "kind": "prompt",
67
+ "id": "judge",
68
+ "name": "LLM as judge",
69
+ "description": "Evaluate an AI-generated answer against a question and a rubric. Returns a structured verdict.",
70
+ "tier": "t1-max",
71
+ "author": "taskclan",
72
+ "version": "1.0.0",
73
+ "tags": [
74
+ "eval",
75
+ "judge"
76
+ ],
77
+ "variables": [
78
+ {
79
+ "name": "question",
80
+ "description": "The original question or prompt the AI was answering.",
81
+ "required": true
82
+ },
83
+ {
84
+ "name": "answer",
85
+ "description": "The AI-generated answer to evaluate.",
86
+ "required": true
87
+ },
88
+ {
89
+ "name": "rubric",
90
+ "description": "Rubric describing what a good answer looks like. Can be a paragraph or a bulleted list.",
91
+ "required": true
92
+ }
93
+ ],
94
+ "template": "You are evaluating an AI-generated answer as a strict but fair judge.\nScore the answer against the rubric. Never side with the answer by\ndefault; look for real failures.\n\n--- QUESTION ---\n{{question}}\n--- END QUESTION ---\n\n--- ANSWER ---\n{{answer}}\n--- END ANSWER ---\n\n--- RUBRIC ---\n{{rubric}}\n--- END RUBRIC ---\n\nRespond with a JSON object exactly matching this shape (no prose):\n\n {\n \"verdict\": \"pass\" | \"fail\" | \"partial\",\n \"score\": 0-10 integer,\n \"reasoning\": \"one short paragraph\",\n \"failures\": [\"specific issue 1\", \"specific issue 2\"]\n }\n"
95
+ },
96
+ {
97
+ "kind": "prompt",
98
+ "id": "refine",
99
+ "name": "Refine text with feedback",
100
+ "description": "Rewrite a draft in response to specific feedback, preserving intent and unaffected sections.",
101
+ "tier": "t1-flow",
102
+ "author": "taskclan",
103
+ "version": "1.0.0",
104
+ "tags": [
105
+ "refine",
106
+ "editing"
107
+ ],
108
+ "variables": [
109
+ {
110
+ "name": "draft",
111
+ "description": "The current text to refine.",
112
+ "required": true
113
+ },
114
+ {
115
+ "name": "feedback",
116
+ "description": "The specific changes to apply. Can be a bulleted list or a paragraph.",
117
+ "required": true
118
+ },
119
+ {
120
+ "name": "preserve",
121
+ "description": "What must NOT change. E.g. \"keep all code snippets exactly as-is\", \"keep the ending paragraph\".",
122
+ "required": false,
123
+ "default": "keep everything not explicitly targeted by the feedback"
124
+ }
125
+ ],
126
+ "template": "Refine the draft below by applying the feedback. Follow these rules:\n\n1. Apply every piece of feedback that is actionable. If a feedback item\n is unclear, note it in a one-line \"clarify:\" comment at the end and\n skip it rather than guessing.\n2. Preserve: {{preserve}}\n3. Do not editorialise about the feedback. Output only the refined\n version, followed by any clarify: comments.\n\n--- DRAFT ---\n{{draft}}\n--- END DRAFT ---\n\n--- FEEDBACK ---\n{{feedback}}\n--- END FEEDBACK ---\n"
127
+ },
128
+ {
129
+ "kind": "prompt",
130
+ "id": "summarize",
131
+ "name": "Summarize text",
132
+ "description": "Produce a tight, faithful summary of a body of text at a controllable length and altitude.",
133
+ "tier": "t1-flow",
134
+ "author": "taskclan",
135
+ "version": "1.0.0",
136
+ "tags": [
137
+ "summary",
138
+ "text"
139
+ ],
140
+ "variables": [
141
+ {
142
+ "name": "text",
143
+ "description": "The source text to summarise.",
144
+ "required": true
145
+ },
146
+ {
147
+ "name": "length",
148
+ "description": "Target length. One of \"one sentence\", \"one paragraph\", \"bulleted\", \"5 bullets\", \"detailed\".",
149
+ "required": false,
150
+ "default": "one paragraph"
151
+ },
152
+ {
153
+ "name": "audience",
154
+ "description": "Who the summary is for. Shapes altitude and jargon level. E.g. \"senior engineer\", \"non-technical exec\", \"10 year old\".",
155
+ "required": false,
156
+ "default": "general reader"
157
+ }
158
+ ],
159
+ "template": "Summarise the text below at length \"{{length}}\" for the audience \"{{audience}}\".\n\nRules:\n- Faithful to the source. Do not add facts.\n- Skip generic hedges like \"in conclusion\" or \"overall\".\n- If the source is empty or too short to summarise, say so and stop.\n\n--- SOURCE TEXT ---\n{{text}}\n--- END ---\n"
160
+ },
161
+ {
162
+ "kind": "prompt",
163
+ "id": "translate",
164
+ "name": "Translate text",
165
+ "description": "Translate text into a target language, preserving tone, formatting, and code fences.",
166
+ "tier": "t1-flow",
167
+ "author": "taskclan",
168
+ "version": "1.0.0",
169
+ "tags": [
170
+ "translation",
171
+ "i18n"
172
+ ],
173
+ "variables": [
174
+ {
175
+ "name": "text",
176
+ "description": "Source text.",
177
+ "required": true
178
+ },
179
+ {
180
+ "name": "target_language",
181
+ "description": "The target language. E.g. \"French\", \"Japanese\", \"Yoruba\", \"es-MX\".",
182
+ "required": true
183
+ },
184
+ {
185
+ "name": "tone",
186
+ "description": "Optional tone override. E.g. \"formal\", \"conversational\", \"technical documentation\".",
187
+ "required": false,
188
+ "default": "match the source tone"
189
+ }
190
+ ],
191
+ "template": "Translate the text below into {{target_language}}. Preserve tone\n(\"{{tone}}\"), formatting, and any code fences exactly. Never translate\ncontent inside code fences.\n\nIf a phrase has no faithful equivalent, translate for meaning and add\na one-line footnote at the very end explaining the choice.\n\n--- SOURCE ---\n{{text}}\n--- END ---\n"
192
+ }
193
+ ]
@@ -543,14 +543,16 @@
543
543
  "vscode"
544
544
  ],
545
545
  "author": "taskclan",
546
- "version": "1.0.0",
546
+ "version": "1.1.0",
547
547
  "tags": [
548
548
  "architecture",
549
549
  "design"
550
550
  ],
551
551
  "capabilities": [
552
552
  "tool_use",
553
- "extended_thinking"
553
+ "extended_thinking",
554
+ "vision",
555
+ "documents"
554
556
  ],
555
557
  "system": "You are the Taskclan architect. The user brings a design question or\na system diagram. Respond at the architectural altitude: tradeoffs,\nfailure modes, evolution paths, what to build versus what to buy.\nDo NOT write implementation code unless the user explicitly asks —\nthat is what the /agent slash command is for. Push back when the\ndirection is wrong; a good architect says \"I would not do it this\nway, here is what I would do instead, and here is why\". Ground every\nrecommendation in a concrete second-order effect (\"this saves you\nfrom having to build X later\"). Skip generalities; be specific to\nthe user's stack.\n"
556
558
  },
@@ -581,13 +583,15 @@
581
583
  "vscode"
582
584
  ],
583
585
  "author": "taskclan",
584
- "version": "1.0.0",
586
+ "version": "1.1.0",
585
587
  "tags": [
586
588
  "security",
587
589
  "review"
588
590
  ],
589
591
  "capabilities": [
590
- "tool_use"
592
+ "tool_use",
593
+ "vision",
594
+ "documents"
591
595
  ],
592
596
  "system": "You are the Taskclan security-first reviewer, adversarial mindset.\nEverything the user shows you gets read the way an attacker would\nread it. Look for: injection paths (SQL, command, template, prompt,\nheader), broken or missing auth checks, unsafe deserialisation,\nsecret leakage in logs or responses, weak crypto or timing attacks\non comparisons, SSRF, insecure defaults, race conditions in\nprivileged paths, unbounded resource use, XSS or CSRF surfaces.\nFor every finding: severity (critical / high / medium / low), the\nspecific line or pattern, exactly how an attacker would exploit it,\nand the minimal fix. Rank by severity, critical first. Ignore\nsecurity theatre (\"consider using environment variables\" is NOT a\nfinding). If the code is genuinely clean, say \"no findings\" and stop.\n"
593
597
  },
@@ -601,13 +605,15 @@
601
605
  "vscode"
602
606
  ],
603
607
  "author": "taskclan",
604
- "version": "1.0.0",
608
+ "version": "1.1.0",
605
609
  "tags": [
606
610
  "review",
607
611
  "quality"
608
612
  ],
609
613
  "capabilities": [
610
- "tool_use"
614
+ "tool_use",
615
+ "vision",
616
+ "documents"
611
617
  ],
612
618
  "system": "You are the Taskclan senior reviewer. Everything the user shows you\ngets a staff-engineer code review. Flag: correctness bugs, race\nconditions, obvious perf issues, missing error handling, weak names,\nhidden coupling. Be direct. Rank findings by blast radius, highest\nfirst. Do not comment on style unless it obscures meaning. If the\ncode is fine, say \"no findings\" and stop.\n"
613
619
  },
@@ -628,5 +634,198 @@
628
634
  ],
629
635
  "system": "You are the Taskclan sensei — a patient teacher in the Socratic\ntradition. You never give the answer directly. Every reply is a\nquestion that leads the student one step closer. When the student\nanswers correctly, confirm briefly and ask the next question. When\nthey get stuck, offer a smaller hint, still as a question. Only give\nthe direct answer if the student explicitly asks \"just tell me\" three\ntimes, and even then, follow the answer with \"what do you notice\nabout that?\".\n"
630
636
  }
637
+ ],
638
+ "prompts": [
639
+ {
640
+ "kind": "prompt",
641
+ "id": "classify",
642
+ "name": "Classify into a fixed set",
643
+ "description": "Classify a piece of text into exactly one of a small set of labels. Returns just the label.",
644
+ "tier": "t1-core",
645
+ "author": "taskclan",
646
+ "version": "1.0.0",
647
+ "tags": [
648
+ "classification"
649
+ ],
650
+ "variables": [
651
+ {
652
+ "name": "text",
653
+ "description": "The text to classify.",
654
+ "required": true
655
+ },
656
+ {
657
+ "name": "labels",
658
+ "description": "Comma-separated list of allowed labels. E.g. \"billing, technical, refund, general\".",
659
+ "required": true
660
+ },
661
+ {
662
+ "name": "unknown_label",
663
+ "description": "The label to return when no other label fits. E.g. \"other\" or \"unknown\".",
664
+ "required": false,
665
+ "default": "unknown"
666
+ }
667
+ ],
668
+ "template": "Classify the text below into EXACTLY ONE of these labels:\n{{labels}}\n\nIf none fit, respond with: {{unknown_label}}\n\nRules:\n- Output the single label, lowercase, and NOTHING else.\n- No explanation, no punctuation, no code fences.\n\n--- TEXT ---\n{{text}}\n--- END ---\n"
669
+ },
670
+ {
671
+ "kind": "prompt",
672
+ "id": "extract",
673
+ "name": "Extract structured data",
674
+ "description": "Pull structured fields out of unstructured text into a JSON object matching the shape you describe.",
675
+ "tier": "t1-flow",
676
+ "author": "taskclan",
677
+ "version": "1.0.0",
678
+ "tags": [
679
+ "extraction",
680
+ "structured"
681
+ ],
682
+ "variables": [
683
+ {
684
+ "name": "text",
685
+ "description": "Unstructured source text.",
686
+ "required": true
687
+ },
688
+ {
689
+ "name": "shape",
690
+ "description": "Description of the JSON shape you want out. For example a TypeScript-style type such as \"name: string; email: string; company: string or null\".\n",
691
+ "required": true
692
+ },
693
+ {
694
+ "name": "strict",
695
+ "description": "If \"true\", missing fields become null. If \"false\", the model may infer plausible values from context.",
696
+ "required": false,
697
+ "default": "true"
698
+ }
699
+ ],
700
+ "template": "Read the source text below and extract a JSON object matching this shape:\n\n {{shape}}\n\nRules:\n- Output ONLY the JSON object. No prose, no code fences, no commentary.\n- When strict={{strict}}: for missing fields, use null. Do not invent values.\n- If a value cannot be represented in the given shape, omit the entire object and output {\"_error\": \"reason\"} instead.\n\n--- SOURCE ---\n{{text}}\n--- END ---\n"
701
+ },
702
+ {
703
+ "kind": "prompt",
704
+ "id": "judge",
705
+ "name": "LLM as judge",
706
+ "description": "Evaluate an AI-generated answer against a question and a rubric. Returns a structured verdict.",
707
+ "tier": "t1-max",
708
+ "author": "taskclan",
709
+ "version": "1.0.0",
710
+ "tags": [
711
+ "eval",
712
+ "judge"
713
+ ],
714
+ "variables": [
715
+ {
716
+ "name": "question",
717
+ "description": "The original question or prompt the AI was answering.",
718
+ "required": true
719
+ },
720
+ {
721
+ "name": "answer",
722
+ "description": "The AI-generated answer to evaluate.",
723
+ "required": true
724
+ },
725
+ {
726
+ "name": "rubric",
727
+ "description": "Rubric describing what a good answer looks like. Can be a paragraph or a bulleted list.",
728
+ "required": true
729
+ }
730
+ ],
731
+ "template": "You are evaluating an AI-generated answer as a strict but fair judge.\nScore the answer against the rubric. Never side with the answer by\ndefault; look for real failures.\n\n--- QUESTION ---\n{{question}}\n--- END QUESTION ---\n\n--- ANSWER ---\n{{answer}}\n--- END ANSWER ---\n\n--- RUBRIC ---\n{{rubric}}\n--- END RUBRIC ---\n\nRespond with a JSON object exactly matching this shape (no prose):\n\n {\n \"verdict\": \"pass\" | \"fail\" | \"partial\",\n \"score\": 0-10 integer,\n \"reasoning\": \"one short paragraph\",\n \"failures\": [\"specific issue 1\", \"specific issue 2\"]\n }\n"
732
+ },
733
+ {
734
+ "kind": "prompt",
735
+ "id": "refine",
736
+ "name": "Refine text with feedback",
737
+ "description": "Rewrite a draft in response to specific feedback, preserving intent and unaffected sections.",
738
+ "tier": "t1-flow",
739
+ "author": "taskclan",
740
+ "version": "1.0.0",
741
+ "tags": [
742
+ "refine",
743
+ "editing"
744
+ ],
745
+ "variables": [
746
+ {
747
+ "name": "draft",
748
+ "description": "The current text to refine.",
749
+ "required": true
750
+ },
751
+ {
752
+ "name": "feedback",
753
+ "description": "The specific changes to apply. Can be a bulleted list or a paragraph.",
754
+ "required": true
755
+ },
756
+ {
757
+ "name": "preserve",
758
+ "description": "What must NOT change. E.g. \"keep all code snippets exactly as-is\", \"keep the ending paragraph\".",
759
+ "required": false,
760
+ "default": "keep everything not explicitly targeted by the feedback"
761
+ }
762
+ ],
763
+ "template": "Refine the draft below by applying the feedback. Follow these rules:\n\n1. Apply every piece of feedback that is actionable. If a feedback item\n is unclear, note it in a one-line \"clarify:\" comment at the end and\n skip it rather than guessing.\n2. Preserve: {{preserve}}\n3. Do not editorialise about the feedback. Output only the refined\n version, followed by any clarify: comments.\n\n--- DRAFT ---\n{{draft}}\n--- END DRAFT ---\n\n--- FEEDBACK ---\n{{feedback}}\n--- END FEEDBACK ---\n"
764
+ },
765
+ {
766
+ "kind": "prompt",
767
+ "id": "summarize",
768
+ "name": "Summarize text",
769
+ "description": "Produce a tight, faithful summary of a body of text at a controllable length and altitude.",
770
+ "tier": "t1-flow",
771
+ "author": "taskclan",
772
+ "version": "1.0.0",
773
+ "tags": [
774
+ "summary",
775
+ "text"
776
+ ],
777
+ "variables": [
778
+ {
779
+ "name": "text",
780
+ "description": "The source text to summarise.",
781
+ "required": true
782
+ },
783
+ {
784
+ "name": "length",
785
+ "description": "Target length. One of \"one sentence\", \"one paragraph\", \"bulleted\", \"5 bullets\", \"detailed\".",
786
+ "required": false,
787
+ "default": "one paragraph"
788
+ },
789
+ {
790
+ "name": "audience",
791
+ "description": "Who the summary is for. Shapes altitude and jargon level. E.g. \"senior engineer\", \"non-technical exec\", \"10 year old\".",
792
+ "required": false,
793
+ "default": "general reader"
794
+ }
795
+ ],
796
+ "template": "Summarise the text below at length \"{{length}}\" for the audience \"{{audience}}\".\n\nRules:\n- Faithful to the source. Do not add facts.\n- Skip generic hedges like \"in conclusion\" or \"overall\".\n- If the source is empty or too short to summarise, say so and stop.\n\n--- SOURCE TEXT ---\n{{text}}\n--- END ---\n"
797
+ },
798
+ {
799
+ "kind": "prompt",
800
+ "id": "translate",
801
+ "name": "Translate text",
802
+ "description": "Translate text into a target language, preserving tone, formatting, and code fences.",
803
+ "tier": "t1-flow",
804
+ "author": "taskclan",
805
+ "version": "1.0.0",
806
+ "tags": [
807
+ "translation",
808
+ "i18n"
809
+ ],
810
+ "variables": [
811
+ {
812
+ "name": "text",
813
+ "description": "Source text.",
814
+ "required": true
815
+ },
816
+ {
817
+ "name": "target_language",
818
+ "description": "The target language. E.g. \"French\", \"Japanese\", \"Yoruba\", \"es-MX\".",
819
+ "required": true
820
+ },
821
+ {
822
+ "name": "tone",
823
+ "description": "Optional tone override. E.g. \"formal\", \"conversational\", \"technical documentation\".",
824
+ "required": false,
825
+ "default": "match the source tone"
826
+ }
827
+ ],
828
+ "template": "Translate the text below into {{target_language}}. Preserve tone\n(\"{{tone}}\"), formatting, and any code fences exactly. Never translate\ncontent inside code fences.\n\nIf a phrase has no faithful equivalent, translate for meaning and add\na one-line footnote at the very end explaining the choice.\n\n--- SOURCE ---\n{{text}}\n--- END ---\n"
829
+ }
631
830
  ]
632
831
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taskclan/achilleon",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "description": "Open registry of skills, agents, and prompts loaded by Taskclan Intelligence — and installable in Cursor, Continue.dev, Claude Code, Windsurf, Cline, Zed, or anywhere else you can paste a prompt. Contribute a YAML file to add or improve an entry.",
5
5
  "license": "MIT",
6
6
  "author": "Taskclan Inc.",
@@ -43,6 +43,8 @@
43
43
  "./skills.json": "./dist/skills.json",
44
44
  "./agents": "./dist/agents.json",
45
45
  "./agents.json": "./dist/agents.json",
46
+ "./prompts": "./dist/prompts.json",
47
+ "./prompts.json": "./dist/prompts.json",
46
48
  "./package.json": "./package.json"
47
49
  },
48
50
  "files": [
@@ -52,8 +54,10 @@
52
54
  "dist/registry.json",
53
55
  "dist/skills.json",
54
56
  "dist/agents.json",
57
+ "dist/prompts.json",
55
58
  "skills",
56
59
  "agents",
60
+ "prompts",
57
61
  "schema",
58
62
  "README.md",
59
63
  "LICENSE"
@@ -0,0 +1,32 @@
1
+ id: classify
2
+ name: Classify into a fixed set
3
+ description: Classify a piece of text into exactly one of a small set of labels. Returns just the label.
4
+ tier: t1-core
5
+ author: taskclan
6
+ version: 1.0.0
7
+ tags:
8
+ - classification
9
+ variables:
10
+ - name: text
11
+ description: The text to classify.
12
+ required: true
13
+ - name: labels
14
+ description: Comma-separated list of allowed labels. E.g. "billing, technical, refund, general".
15
+ required: true
16
+ - name: unknown_label
17
+ description: The label to return when no other label fits. E.g. "other" or "unknown".
18
+ required: false
19
+ default: unknown
20
+ template: |
21
+ Classify the text below into EXACTLY ONE of these labels:
22
+ {{labels}}
23
+
24
+ If none fit, respond with: {{unknown_label}}
25
+
26
+ Rules:
27
+ - Output the single label, lowercase, and NOTHING else.
28
+ - No explanation, no punctuation, no code fences.
29
+
30
+ --- TEXT ---
31
+ {{text}}
32
+ --- END ---
@@ -0,0 +1,36 @@
1
+ id: extract
2
+ name: Extract structured data
3
+ description: Pull structured fields out of unstructured text into a JSON object matching the shape you describe.
4
+ tier: t1-flow
5
+ author: taskclan
6
+ version: 1.0.0
7
+ tags:
8
+ - extraction
9
+ - structured
10
+ variables:
11
+ - name: text
12
+ description: Unstructured source text.
13
+ required: true
14
+ - name: shape
15
+ description: >
16
+ Description of the JSON shape you want out.
17
+ For example a TypeScript-style type such as
18
+ "name: string; email: string; company: string or null".
19
+ required: true
20
+ - name: strict
21
+ description: If "true", missing fields become null. If "false", the model may infer plausible values from context.
22
+ required: false
23
+ default: "true"
24
+ template: |
25
+ Read the source text below and extract a JSON object matching this shape:
26
+
27
+ {{shape}}
28
+
29
+ Rules:
30
+ - Output ONLY the JSON object. No prose, no code fences, no commentary.
31
+ - When strict={{strict}}: for missing fields, use null. Do not invent values.
32
+ - If a value cannot be represented in the given shape, omit the entire object and output {"_error": "reason"} instead.
33
+
34
+ --- SOURCE ---
35
+ {{text}}
36
+ --- END ---
@@ -0,0 +1,44 @@
1
+ id: judge
2
+ name: LLM as judge
3
+ description: Evaluate an AI-generated answer against a question and a rubric. Returns a structured verdict.
4
+ tier: t1-max
5
+ author: taskclan
6
+ version: 1.0.0
7
+ tags:
8
+ - eval
9
+ - judge
10
+ variables:
11
+ - name: question
12
+ description: The original question or prompt the AI was answering.
13
+ required: true
14
+ - name: answer
15
+ description: The AI-generated answer to evaluate.
16
+ required: true
17
+ - name: rubric
18
+ description: Rubric describing what a good answer looks like. Can be a paragraph or a bulleted list.
19
+ required: true
20
+ template: |
21
+ You are evaluating an AI-generated answer as a strict but fair judge.
22
+ Score the answer against the rubric. Never side with the answer by
23
+ default; look for real failures.
24
+
25
+ --- QUESTION ---
26
+ {{question}}
27
+ --- END QUESTION ---
28
+
29
+ --- ANSWER ---
30
+ {{answer}}
31
+ --- END ANSWER ---
32
+
33
+ --- RUBRIC ---
34
+ {{rubric}}
35
+ --- END RUBRIC ---
36
+
37
+ Respond with a JSON object exactly matching this shape (no prose):
38
+
39
+ {
40
+ "verdict": "pass" | "fail" | "partial",
41
+ "score": 0-10 integer,
42
+ "reasoning": "one short paragraph",
43
+ "failures": ["specific issue 1", "specific issue 2"]
44
+ }
@@ -0,0 +1,37 @@
1
+ id: refine
2
+ name: Refine text with feedback
3
+ description: Rewrite a draft in response to specific feedback, preserving intent and unaffected sections.
4
+ tier: t1-flow
5
+ author: taskclan
6
+ version: 1.0.0
7
+ tags:
8
+ - refine
9
+ - editing
10
+ variables:
11
+ - name: draft
12
+ description: The current text to refine.
13
+ required: true
14
+ - name: feedback
15
+ description: The specific changes to apply. Can be a bulleted list or a paragraph.
16
+ required: true
17
+ - name: preserve
18
+ description: What must NOT change. E.g. "keep all code snippets exactly as-is", "keep the ending paragraph".
19
+ required: false
20
+ default: keep everything not explicitly targeted by the feedback
21
+ template: |
22
+ Refine the draft below by applying the feedback. Follow these rules:
23
+
24
+ 1. Apply every piece of feedback that is actionable. If a feedback item
25
+ is unclear, note it in a one-line "clarify:" comment at the end and
26
+ skip it rather than guessing.
27
+ 2. Preserve: {{preserve}}
28
+ 3. Do not editorialise about the feedback. Output only the refined
29
+ version, followed by any clarify: comments.
30
+
31
+ --- DRAFT ---
32
+ {{draft}}
33
+ --- END DRAFT ---
34
+
35
+ --- FEEDBACK ---
36
+ {{feedback}}
37
+ --- END FEEDBACK ---
@@ -0,0 +1,32 @@
1
+ id: summarize
2
+ name: Summarize text
3
+ description: Produce a tight, faithful summary of a body of text at a controllable length and altitude.
4
+ tier: t1-flow
5
+ author: taskclan
6
+ version: 1.0.0
7
+ tags:
8
+ - summary
9
+ - text
10
+ variables:
11
+ - name: text
12
+ description: The source text to summarise.
13
+ required: true
14
+ - name: length
15
+ description: Target length. One of "one sentence", "one paragraph", "bulleted", "5 bullets", "detailed".
16
+ required: false
17
+ default: one paragraph
18
+ - name: audience
19
+ description: Who the summary is for. Shapes altitude and jargon level. E.g. "senior engineer", "non-technical exec", "10 year old".
20
+ required: false
21
+ default: general reader
22
+ template: |
23
+ Summarise the text below at length "{{length}}" for the audience "{{audience}}".
24
+
25
+ Rules:
26
+ - Faithful to the source. Do not add facts.
27
+ - Skip generic hedges like "in conclusion" or "overall".
28
+ - If the source is empty or too short to summarise, say so and stop.
29
+
30
+ --- SOURCE TEXT ---
31
+ {{text}}
32
+ --- END ---
@@ -0,0 +1,31 @@
1
+ id: translate
2
+ name: Translate text
3
+ description: Translate text into a target language, preserving tone, formatting, and code fences.
4
+ tier: t1-flow
5
+ author: taskclan
6
+ version: 1.0.0
7
+ tags:
8
+ - translation
9
+ - i18n
10
+ variables:
11
+ - name: text
12
+ description: Source text.
13
+ required: true
14
+ - name: target_language
15
+ description: The target language. E.g. "French", "Japanese", "Yoruba", "es-MX".
16
+ required: true
17
+ - name: tone
18
+ description: Optional tone override. E.g. "formal", "conversational", "technical documentation".
19
+ required: false
20
+ default: match the source tone
21
+ template: |
22
+ Translate the text below into {{target_language}}. Preserve tone
23
+ ("{{tone}}"), formatting, and any code fences exactly. Never translate
24
+ content inside code fences.
25
+
26
+ If a phrase has no faithful equivalent, translate for meaning and add
27
+ a one-line footnote at the very end explaining the choice.
28
+
29
+ --- SOURCE ---
30
+ {{text}}
31
+ --- END ---
@@ -49,9 +49,9 @@
49
49
  "uniqueItems": true,
50
50
  "items": {
51
51
  "type": "string",
52
- "enum": ["tool_use", "vision", "extended_thinking"]
52
+ "enum": ["tool_use", "vision", "extended_thinking", "video", "audio", "documents"]
53
53
  },
54
- "description": "Optional flags the runtime honours (e.g. enable tool calls, allow image input, request thinking blocks)."
54
+ "description": "Optional flags the runtime honours (e.g. enable tool calls, allow image / video / audio input, accept document uploads, request thinking blocks). Video and audio are routed through the tier's video/audio primary at runtime (T1 Max only)."
55
55
  },
56
56
  "author": {
57
57
  "type": "string",
@@ -0,0 +1,84 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://taskclan.com/achilleon/schema/prompt.schema.json",
4
+ "title": "Achilleon Prompt",
5
+ "description": "A parameterised prompt template loaded by SDK consumers. Different from a skill (fixed system prompt for a slash command) — prompts take user-supplied values and render a final prompt string at call time. Lives at prompts/{id}.yml.",
6
+ "type": "object",
7
+ "required": ["id", "name", "description", "template"],
8
+ "additionalProperties": false,
9
+ "properties": {
10
+ "id": {
11
+ "type": "string",
12
+ "pattern": "^[a-z][a-z0-9-]{1,39}$",
13
+ "description": "Lowercase, hyphen-separated. Must match the filename (without .yml). 2-40 characters."
14
+ },
15
+ "name": {
16
+ "type": "string",
17
+ "minLength": 2,
18
+ "maxLength": 60,
19
+ "description": "Human-readable name shown in docs and pickers."
20
+ },
21
+ "description": {
22
+ "type": "string",
23
+ "minLength": 8,
24
+ "maxLength": 200,
25
+ "description": "One-line description of what the prompt does."
26
+ },
27
+ "template": {
28
+ "type": "string",
29
+ "minLength": 20,
30
+ "maxLength": 8000,
31
+ "description": "The prompt body. Placeholders use {{variable}} syntax. Every placeholder must have a matching entry in `variables`; the validator enforces this."
32
+ },
33
+ "variables": {
34
+ "type": "array",
35
+ "maxItems": 20,
36
+ "items": {
37
+ "type": "object",
38
+ "required": ["name", "description"],
39
+ "additionalProperties": false,
40
+ "properties": {
41
+ "name": {
42
+ "type": "string",
43
+ "pattern": "^[a-z][a-z_0-9]{0,39}$",
44
+ "description": "Variable name as it appears in the template ({{name}}). snake_case."
45
+ },
46
+ "description": {
47
+ "type": "string",
48
+ "minLength": 4,
49
+ "maxLength": 200,
50
+ "description": "What the variable is for. Shown in docs + used by SDK tooling."
51
+ },
52
+ "required": {
53
+ "type": "boolean",
54
+ "default": true,
55
+ "description": "If false, the SDK renderer allows omission (empty string is substituted)."
56
+ },
57
+ "default": {
58
+ "type": "string",
59
+ "description": "Default value substituted when a required variable is missing but a default is provided."
60
+ }
61
+ }
62
+ }
63
+ },
64
+ "tier": {
65
+ "type": "string",
66
+ "enum": ["t1-core", "t1-flow", "t1-max", "t1-auto"],
67
+ "description": "Recommended T1 tier this prompt runs well on. Consumers can override."
68
+ },
69
+ "author": {
70
+ "type": "string",
71
+ "pattern": "^[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,38}[a-zA-Z0-9])?$"
72
+ },
73
+ "version": {
74
+ "type": "string",
75
+ "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)$"
76
+ },
77
+ "tags": {
78
+ "type": "array",
79
+ "items": { "type": "string", "minLength": 2, "maxLength": 30 },
80
+ "maxItems": 10,
81
+ "uniqueItems": true
82
+ }
83
+ }
84
+ }