@taskclan/achilleon 0.3.0 → 0.4.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.
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
@@ -44,13 +44,44 @@ export interface AchilleonAgent {
44
44
 
45
45
  export type AchilleonEntry = AchilleonSkill | AchilleonAgent;
46
46
 
47
+ export interface AchilleonPromptVariable {
48
+ name: string;
49
+ description: string;
50
+ required?: boolean;
51
+ default?: string;
52
+ }
53
+
54
+ export interface AchilleonPrompt {
55
+ kind: 'prompt';
56
+ id: string;
57
+ name: string;
58
+ description: string;
59
+ template: string;
60
+ variables?: AchilleonPromptVariable[];
61
+ tier?: SkillTier;
62
+ author?: string;
63
+ version?: string;
64
+ tags?: string[];
65
+ }
66
+
47
67
  export const skills: AchilleonSkill[];
48
68
  export const agents: AchilleonAgent[];
69
+ export const prompts: AchilleonPrompt[];
49
70
  /** Agents first, then skills, in the order they were loaded. */
50
71
  export const entries: AchilleonEntry[];
51
72
 
52
73
  export function getSkill(id: string): AchilleonSkill | undefined;
53
74
  export function getAgent(id: string): AchilleonAgent | undefined;
54
75
  export function getEntry(id: string): AchilleonEntry | undefined;
76
+ export function getPrompt(id: string): AchilleonPrompt | undefined;
55
77
  export function categories(): SkillCategory[];
56
78
  export function byTier(tier: SkillTier): AchilleonEntry[];
79
+
80
+ /**
81
+ * Render a prompt template with values. Missing required variables throw;
82
+ * missing optional variables substitute the empty string.
83
+ *
84
+ * import { render } from '@taskclan/achilleon';
85
+ * const filled = render('summarize', { text: articleBody, length: '5 bullets' });
86
+ */
87
+ 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
+ ]
@@ -628,5 +628,198 @@
628
628
  ],
629
629
  "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
630
  }
631
+ ],
632
+ "prompts": [
633
+ {
634
+ "kind": "prompt",
635
+ "id": "classify",
636
+ "name": "Classify into a fixed set",
637
+ "description": "Classify a piece of text into exactly one of a small set of labels. Returns just the label.",
638
+ "tier": "t1-core",
639
+ "author": "taskclan",
640
+ "version": "1.0.0",
641
+ "tags": [
642
+ "classification"
643
+ ],
644
+ "variables": [
645
+ {
646
+ "name": "text",
647
+ "description": "The text to classify.",
648
+ "required": true
649
+ },
650
+ {
651
+ "name": "labels",
652
+ "description": "Comma-separated list of allowed labels. E.g. \"billing, technical, refund, general\".",
653
+ "required": true
654
+ },
655
+ {
656
+ "name": "unknown_label",
657
+ "description": "The label to return when no other label fits. E.g. \"other\" or \"unknown\".",
658
+ "required": false,
659
+ "default": "unknown"
660
+ }
661
+ ],
662
+ "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"
663
+ },
664
+ {
665
+ "kind": "prompt",
666
+ "id": "extract",
667
+ "name": "Extract structured data",
668
+ "description": "Pull structured fields out of unstructured text into a JSON object matching the shape you describe.",
669
+ "tier": "t1-flow",
670
+ "author": "taskclan",
671
+ "version": "1.0.0",
672
+ "tags": [
673
+ "extraction",
674
+ "structured"
675
+ ],
676
+ "variables": [
677
+ {
678
+ "name": "text",
679
+ "description": "Unstructured source text.",
680
+ "required": true
681
+ },
682
+ {
683
+ "name": "shape",
684
+ "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",
685
+ "required": true
686
+ },
687
+ {
688
+ "name": "strict",
689
+ "description": "If \"true\", missing fields become null. If \"false\", the model may infer plausible values from context.",
690
+ "required": false,
691
+ "default": "true"
692
+ }
693
+ ],
694
+ "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"
695
+ },
696
+ {
697
+ "kind": "prompt",
698
+ "id": "judge",
699
+ "name": "LLM as judge",
700
+ "description": "Evaluate an AI-generated answer against a question and a rubric. Returns a structured verdict.",
701
+ "tier": "t1-max",
702
+ "author": "taskclan",
703
+ "version": "1.0.0",
704
+ "tags": [
705
+ "eval",
706
+ "judge"
707
+ ],
708
+ "variables": [
709
+ {
710
+ "name": "question",
711
+ "description": "The original question or prompt the AI was answering.",
712
+ "required": true
713
+ },
714
+ {
715
+ "name": "answer",
716
+ "description": "The AI-generated answer to evaluate.",
717
+ "required": true
718
+ },
719
+ {
720
+ "name": "rubric",
721
+ "description": "Rubric describing what a good answer looks like. Can be a paragraph or a bulleted list.",
722
+ "required": true
723
+ }
724
+ ],
725
+ "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"
726
+ },
727
+ {
728
+ "kind": "prompt",
729
+ "id": "refine",
730
+ "name": "Refine text with feedback",
731
+ "description": "Rewrite a draft in response to specific feedback, preserving intent and unaffected sections.",
732
+ "tier": "t1-flow",
733
+ "author": "taskclan",
734
+ "version": "1.0.0",
735
+ "tags": [
736
+ "refine",
737
+ "editing"
738
+ ],
739
+ "variables": [
740
+ {
741
+ "name": "draft",
742
+ "description": "The current text to refine.",
743
+ "required": true
744
+ },
745
+ {
746
+ "name": "feedback",
747
+ "description": "The specific changes to apply. Can be a bulleted list or a paragraph.",
748
+ "required": true
749
+ },
750
+ {
751
+ "name": "preserve",
752
+ "description": "What must NOT change. E.g. \"keep all code snippets exactly as-is\", \"keep the ending paragraph\".",
753
+ "required": false,
754
+ "default": "keep everything not explicitly targeted by the feedback"
755
+ }
756
+ ],
757
+ "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"
758
+ },
759
+ {
760
+ "kind": "prompt",
761
+ "id": "summarize",
762
+ "name": "Summarize text",
763
+ "description": "Produce a tight, faithful summary of a body of text at a controllable length and altitude.",
764
+ "tier": "t1-flow",
765
+ "author": "taskclan",
766
+ "version": "1.0.0",
767
+ "tags": [
768
+ "summary",
769
+ "text"
770
+ ],
771
+ "variables": [
772
+ {
773
+ "name": "text",
774
+ "description": "The source text to summarise.",
775
+ "required": true
776
+ },
777
+ {
778
+ "name": "length",
779
+ "description": "Target length. One of \"one sentence\", \"one paragraph\", \"bulleted\", \"5 bullets\", \"detailed\".",
780
+ "required": false,
781
+ "default": "one paragraph"
782
+ },
783
+ {
784
+ "name": "audience",
785
+ "description": "Who the summary is for. Shapes altitude and jargon level. E.g. \"senior engineer\", \"non-technical exec\", \"10 year old\".",
786
+ "required": false,
787
+ "default": "general reader"
788
+ }
789
+ ],
790
+ "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"
791
+ },
792
+ {
793
+ "kind": "prompt",
794
+ "id": "translate",
795
+ "name": "Translate text",
796
+ "description": "Translate text into a target language, preserving tone, formatting, and code fences.",
797
+ "tier": "t1-flow",
798
+ "author": "taskclan",
799
+ "version": "1.0.0",
800
+ "tags": [
801
+ "translation",
802
+ "i18n"
803
+ ],
804
+ "variables": [
805
+ {
806
+ "name": "text",
807
+ "description": "Source text.",
808
+ "required": true
809
+ },
810
+ {
811
+ "name": "target_language",
812
+ "description": "The target language. E.g. \"French\", \"Japanese\", \"Yoruba\", \"es-MX\".",
813
+ "required": true
814
+ },
815
+ {
816
+ "name": "tone",
817
+ "description": "Optional tone override. E.g. \"formal\", \"conversational\", \"technical documentation\".",
818
+ "required": false,
819
+ "default": "match the source tone"
820
+ }
821
+ ],
822
+ "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"
823
+ }
631
824
  ]
632
825
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taskclan/achilleon",
3
- "version": "0.3.0",
3
+ "version": "0.4.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 ---
@@ -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
+ }