@neurodock/core 0.0.1 → 0.2.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 (57) hide show
  1. package/LICENSE +657 -7
  2. package/README.md +50 -3
  3. package/data/neurotype-addenda/v1.json +389 -0
  4. package/dist/index.d.ts +5 -0
  5. package/dist/index.d.ts.map +1 -0
  6. package/dist/index.js +7 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/index.test.d.ts +2 -0
  9. package/dist/index.test.d.ts.map +1 -0
  10. package/dist/index.test.js +29 -0
  11. package/dist/index.test.js.map +1 -0
  12. package/dist/neurotype-addenda.d.ts +91 -0
  13. package/dist/neurotype-addenda.d.ts.map +1 -0
  14. package/dist/neurotype-addenda.js +175 -0
  15. package/dist/neurotype-addenda.js.map +1 -0
  16. package/dist/neurotype-addenda.parity.test.d.ts +2 -0
  17. package/dist/neurotype-addenda.parity.test.d.ts.map +1 -0
  18. package/dist/neurotype-addenda.parity.test.js +69 -0
  19. package/dist/neurotype-addenda.parity.test.js.map +1 -0
  20. package/dist/neurotype-addenda.schema.test.d.ts +2 -0
  21. package/dist/neurotype-addenda.schema.test.d.ts.map +1 -0
  22. package/dist/neurotype-addenda.schema.test.js +83 -0
  23. package/dist/neurotype-addenda.schema.test.js.map +1 -0
  24. package/dist/neurotype-addenda.test.d.ts +2 -0
  25. package/dist/neurotype-addenda.test.d.ts.map +1 -0
  26. package/dist/neurotype-addenda.test.js +165 -0
  27. package/dist/neurotype-addenda.test.js.map +1 -0
  28. package/dist/profile.d.ts +128 -0
  29. package/dist/profile.d.ts.map +1 -0
  30. package/dist/profile.js +27 -0
  31. package/dist/profile.js.map +1 -0
  32. package/dist/profile.presets.test.d.ts +2 -0
  33. package/dist/profile.presets.test.d.ts.map +1 -0
  34. package/dist/profile.presets.test.js +43 -0
  35. package/dist/profile.presets.test.js.map +1 -0
  36. package/dist/profile.schema.test.d.ts +2 -0
  37. package/dist/profile.schema.test.d.ts.map +1 -0
  38. package/dist/profile.schema.test.js +282 -0
  39. package/dist/profile.schema.test.js.map +1 -0
  40. package/dist/profile.test.d.ts +2 -0
  41. package/dist/profile.test.d.ts.map +1 -0
  42. package/dist/profile.test.js +60 -0
  43. package/dist/profile.test.js.map +1 -0
  44. package/dist/test-helpers/ajv.d.ts +22 -0
  45. package/dist/test-helpers/ajv.d.ts.map +1 -0
  46. package/dist/test-helpers/ajv.js +38 -0
  47. package/dist/test-helpers/ajv.js.map +1 -0
  48. package/package.json +16 -9
  49. package/schemas/neurotype-addenda.schema.json +167 -0
  50. package/schemas/plugin.example.yaml +116 -71
  51. package/schemas/plugin.minimal.yaml +18 -12
  52. package/schemas/plugin.schema.json +13 -2
  53. package/schemas/profile.example.yaml +112 -64
  54. package/schemas/profile.minimal.yaml +15 -7
  55. package/schemas/profile.schema.json +116 -0
  56. package/src/index.test.ts +0 -8
  57. package/src/index.ts +0 -1
package/README.md CHANGED
@@ -1,7 +1,54 @@
1
1
  # @neurodock/core
2
2
 
3
- Shared types, profile schema, and plugin protocol for NeuroDock.
3
+ Shared, framework-neutral building blocks for NeuroDock: the profile JSON Schema and
4
+ TypeScript types, the plugin protocol schema, and the per-neurotype prompt-shaping
5
+ artifact + assembler that every NeuroDock surface uses to tailor output.
4
6
 
5
- This package is a Phase 0 stub. Real implementation lands in Phase 1.
7
+ This package has zero runtime dependencies. Data lives as JSON (validated by JSON Schema);
8
+ the assembler is a pure function.
6
9
 
7
- See Sections 5 and 6 for scope.
10
+ ## Install
11
+
12
+ ```sh
13
+ pnpm add @neurodock/core
14
+ ```
15
+
16
+ ## What's inside
17
+
18
+ - **Profile types + schema** — `Profile` (and its block interfaces) plus
19
+ `schemas/profile.schema.json`, the cross-cutting user manifest read by every MCP server
20
+ and skill. See [ADR 0004](https://github.com/tlennon-ie/neurodock/blob/main/docs/decisions/0004-profile-schema-design.md).
21
+ - **Neurotype-shaping artifact + assembler** — `data/neurotype-addenda/v1.json` is the
22
+ single source of truth for the per-(tool × neurotype) prompt addenda; `assembleNeurotypeAddendum`
23
+ turns it into the addendum string. Both the browser extension and the `mcp-translation`
24
+ server read this artifact so per-neurotype shaping stays identical across surfaces. See
25
+ [ADR 0012](https://github.com/tlennon-ie/neurodock/blob/main/docs/decisions/0012-shared-neurotype-shaping-layer.md).
26
+ - **Plugin protocol schema** — `schemas/plugin.schema.json`. See
27
+ [ADR 0007](https://github.com/tlennon-ie/neurodock/blob/main/docs/decisions/0007-plugin-protocol.md).
28
+
29
+ ## Usage
30
+
31
+ ```ts
32
+ import {
33
+ assembleNeurotypeAddendum,
34
+ neurotypeAddendaV1,
35
+ type Profile,
36
+ } from "@neurodock/core";
37
+
38
+ const addendum = assembleNeurotypeAddendum(neurotypeAddendaV1, {
39
+ tool: "translate_incoming",
40
+ neurotypes: ["adhd", "asd"], // fuses to AuDHD
41
+ outputFormat: "answer_first",
42
+ maxChunkSize: 5,
43
+ voiceInputPreferred: false,
44
+ });
45
+ // `addendum` is appended after the JSON schema block of a model prompt.
46
+ // An empty input (no neurotypes, all defaults) returns "".
47
+ ```
48
+
49
+ The addendum is **content**, not schema shape: it never changes a tool's output schema
50
+ (per [ADR 0011](https://github.com/tlennon-ie/neurodock/blob/main/docs/decisions/0011-neurotype-schema-strategy.md)).
51
+
52
+ ## License
53
+
54
+ AGPL-3.0-or-later
@@ -0,0 +1,389 @@
1
+ {
2
+ "$schema": "https://schemas.neurodock.org/neurotype-addenda/v1/neurotype-addenda.schema.json",
3
+ "artifact_version": "1.0.0",
4
+ "description": "Language-neutral, enum-keyed prompt-shaping content for per-neurotype response tailoring. Single source of truth shared by the NeuroDock browser extension and (in a later PR) the mcp-translation server. This artifact is CONTENT, not schema shape (ADR 0011): it carries no field constraints, only the prose blocks that get appended to a model prompt. The only interpolation tokens are {max_chunk_size} and {notes}.",
5
+ "tokens": ["{max_chunk_size}", "{notes}"],
6
+ "fusion": {
7
+ "description": "AuDHD substitution rule. When the input set lists `audhd` directly, OR lists both `adhd` and `asd`, the fused `audhd` block is substituted: `audhd` is added and `adhd` + `asd` are removed. De-duplication is implied by set semantics.",
8
+ "result": "audhd",
9
+ "any_of": ["audhd"],
10
+ "all_of": ["adhd", "asd"],
11
+ "remove": ["adhd", "asd"]
12
+ },
13
+ "priority": [
14
+ "tourette",
15
+ "dyspraxia",
16
+ "dyslexia",
17
+ "ocd",
18
+ "asd",
19
+ "adhd",
20
+ "audhd",
21
+ "other"
22
+ ],
23
+ "framing": {
24
+ "wrapper_prefix": "\n\n",
25
+ "wrapper_suffix": "\n",
26
+ "section_separator": "\n",
27
+ "block_line_separator": "\n",
28
+ "header": [
29
+ "---",
30
+ "## Reader-specific overrides (apply LAST, after the schema)",
31
+ "",
32
+ "The reader has these preferences. Honor them inside the schema-shaped JSON you're about to write. If a preference conflicts with the schema, the schema wins — but apply every preference that's compatible.",
33
+ ""
34
+ ],
35
+ "footer": ["---"],
36
+ "conflict_footer_min_neurotypes": 3,
37
+ "conflict_footer": "When these preferences conflict (e.g. 'literal' vs 'soft'), prefer the more conservative reading: shorter, more concrete, less pressure."
38
+ },
39
+ "output_format": {
40
+ "prefix": "Output shape: ",
41
+ "separator": " — ",
42
+ "descriptions": {
43
+ "answer_first": "Lead every prose field with the headline conclusion. Reasoning afterwards if at all.",
44
+ "conventional": "Brief context first, then the verdict.",
45
+ "bullet_first": "Lead with a short bullet list before any prose."
46
+ },
47
+ "default": "answer_first"
48
+ },
49
+ "voice_input": {
50
+ "block": [
51
+ "Reader preferences (voice input):",
52
+ "- The reader dictates and cannot cheaply hand-edit fiddly punctuation.",
53
+ "- Keep any example, draft, or snippet as a single, copy-pasteable block — do not scatter editable fragments across the response."
54
+ ]
55
+ },
56
+ "tourette": {
57
+ "block": [
58
+ "Reader preferences (Tourette):",
59
+ "- Be concise. Lead with the answer first; keep prose to the fewest sentences that still carry the meaning.",
60
+ "- Cut throat-clearing, recap, and padding. Shorter responses hold the reader's attention for less time.",
61
+ "- Plain, low-pressure phrasing. Avoid manufactured urgency the source did not have.",
62
+ "- Keep a neutral register. Do not add reassurance, soothing language, or motivational pep — it is not wanted here.",
63
+ "- Do not comment on, notice, or reference the reader's behaviour, composure, or movement anywhere in any field."
64
+ ]
65
+ },
66
+ "other": {
67
+ "block": [
68
+ "Reader preferences (self-described):",
69
+ "- Treat the notes below as a literal instruction set from the reader. Honour any 'please do' / 'please don't' requests.",
70
+ "",
71
+ "Reader's own notes:",
72
+ "{notes}"
73
+ ]
74
+ },
75
+ "generic": {
76
+ "adhd": [
77
+ "Reader preferences (ADHD):",
78
+ "- Lead with the verdict in the first phrase of any free-text field. No throat-clearing.",
79
+ "- Keep prose fields to 1-2 short sentences. Cut qualifiers ('perhaps', 'it seems that', 'arguably').",
80
+ "- Cap any list you return at {max_chunk_size} items even if more would fit the schema. Rank by importance and stop.",
81
+ "- No nested clauses. One idea per sentence.",
82
+ "- If you would say 'however' or 'that said', just start a new sentence.",
83
+ "- Concrete verbs over abstract nouns. 'Send the spec' not 'completion of the spec distribution'."
84
+ ],
85
+ "asd": [
86
+ "Reader preferences (autism):",
87
+ "- State subtext literally. Do not soften with 'perhaps' or 'they might' if the evidence in the text is concrete enough to commit. Use 'the sender wants X' when the text supports it.",
88
+ "- No idioms. Banned phrases include: 'touch base', 'circle back', 'loop in', 'ping you', 'moving forward', 'low-hanging fruit', 'ducks in a row', 'reach out', 'hop on a call', 'in the loop', 'out of pocket'.",
89
+ "- If the input message contains an idiom, decode it literally in your prose ('ping you' -> 'send you a message').",
90
+ "- If a sentence depends on tone of voice or facial expression that text cannot carry, flag that explicitly ('text alone is ambiguous — could be sincere or sarcastic').",
91
+ "- Quote the source verbatim in parentheses when you paraphrase a decision or ask.",
92
+ "- No metaphor. No analogies. No 'kind of' / 'sort of' hedges."
93
+ ],
94
+ "audhd": [
95
+ "Reader preferences (AuDHD):",
96
+ "- Lead with the verdict in the first phrase. Literal first sentence, no throat-clearing.",
97
+ "- One idea per sentence; cap list fields at {max_chunk_size} items.",
98
+ "- No idioms. Banned phrases include: 'touch base', 'circle back', 'loop in', 'ping', 'moving forward', 'low-hanging fruit', 'reach out', 'hop on a call'.",
99
+ "- State subtext as commitments when the text supports it ('the sender wants X'), not as guesses ('they might want X') — but when the text is genuinely ambiguous, say so explicitly rather than picking one reading.",
100
+ "- Concrete verbs over abstract nouns; quote the source verbatim in parentheses when paraphrasing a decision.",
101
+ "- If a sentence depends on tone of voice the text cannot carry, flag that explicitly."
102
+ ],
103
+ "ocd": [
104
+ "Reader preferences (OCD):",
105
+ "- Low-pressure phrasing in any 'what to do' field. Avoid: 'urgent', 'must', 'immediately', 'right away', 'ASAP', 'critical', 'before it's too late', 'don't forget', 'make sure'.",
106
+ "- Prefer: 'when you have a minute', 'no rush — when ready', 'consider', 'you may want to'.",
107
+ "- Do not invent ambiguity or risk that is not in the source. If the message has no actionable issue, say so plainly ('nothing here needs an answer today').",
108
+ "- When ranking items, prefer the lowest-pressure framing first.",
109
+ "- Do not amplify time pressure. If the sender said 'soon', do not paraphrase as 'urgently'.",
110
+ "- Never use the word 'wrong' about the user's draft. Use 'reads as' or 'may land as'."
111
+ ],
112
+ "dyslexia": [
113
+ "Reader preferences (dyslexia):",
114
+ "- Short sentences. Maximum 15 words per sentence. Break compound sentences into two.",
115
+ "- Plain words. Replace 'paraphrased' with 'said plainly', 'ambiguous' with 'unclear', 'verbatim' with 'word-for-word', 'register' with 'tone', 'subtext' with 'what they really mean'.",
116
+ "- One idea per sentence. No semicolons. No em-dashes inside sentences.",
117
+ "- Cap any list at {max_chunk_size} items.",
118
+ "- Free-text prose fields: 1 sentence where the schema permits 2-3.",
119
+ "- Common words over rare ones. 'Use' not 'utilise'. 'Help' not 'facilitate'. 'About' not 'regarding'."
120
+ ],
121
+ "dyspraxia": [
122
+ "Reader preferences (dyspraxia):",
123
+ "- Minimise sequencing burden. Give absolute time markers ('Wednesday 28 May', 'by end of this week') rather than relative ones ('next sprint', 'soon').",
124
+ "- For meeting briefs: if an ask references another ask or decision, name it explicitly rather than saying 'the above' or 'as mentioned earlier'.",
125
+ "- For rewrites: prefer one continuous instruction over a numbered sequence when the action is single-step.",
126
+ "- Cap any list at {max_chunk_size} items.",
127
+ "- Group related items together; do not interleave asks from different topics."
128
+ ]
129
+ },
130
+ "tools": {
131
+ "describe_image": {
132
+ "adhd": [
133
+ "Reader preferences (ADHD) — describe_image:",
134
+ "- INVARIANT: Always populate `content_translation` with one entry per logical item in the image (numbered points, bullets, framework slots, chart series, document sections). Use null ONLY if the image is a decorative avatar/logo/icon with no readable instructional content.",
135
+ "- `content_translation`: cap entries at {max_chunk_size}. Each facet text is verb-led and 8 words or fewer ('Wait 5 seconds.', not 'It might be advisable to wait roughly five seconds'). Prefer `action` + `goal` facets — those are what the ADHD reader scans for.",
136
+ "- 'description': 1 sentence (metadata field). Lead with the inferred purpose in 4-7 words, then what's visually shown.",
137
+ "- 'key_elements': cap at {max_chunk_size}. Most prominent first. Stop ranking when you'd be reaching.",
138
+ "- 'inferred_purpose': a noun phrase, not a sentence. 'Promotional slide' not 'This is a promotional slide that…'.",
139
+ "- No qualifiers ('perhaps', 'appears to', 'seems'). Commit, or flag the uncertainty in 'accessibility_notes'.",
140
+ "- 'accessibility_notes': 1 sentence max. Skip if there is nothing assistive-tech-relevant to add."
141
+ ],
142
+ "asd": [
143
+ "Reader preferences (autism) — describe_image:",
144
+ "- INVARIANT: Always populate `content_translation` with one entry per logical item in the image (numbered points, bullets, framework slots, chart series, document sections). Use null ONLY if the image is a decorative avatar/logo/icon with no readable instructional content.",
145
+ "- `content_translation`: each facet text is a literal commitment, no idioms. Quote the source label verbatim in the 'label' field (e.g. label = '1. Emotional Control (The \"Pause\" Strategy)' exactly as the image renders it). Decode any source idiom literally inside facets ('touch base' → 'send a follow-up message').",
146
+ "- Build 'key_elements' (literal nouns + adjectives) BEFORE writing 'description'. The description then references those elements in visual-prominence order, without inferring meaning.",
147
+ "- 'inferred_purpose': if text + visual evidence doesn't make purpose unambiguous, write 'I can't tell from the image alone' and explain in 'accessibility_notes' what would resolve it (e.g. the surrounding page text).",
148
+ "- Banned soft hedges in 'description': 'kind of', 'sort of', 'looks like'. Use 'shows' or 'contains' instead.",
149
+ "- 'transcribed_text': exact verbatim. If partially obscured, transcribe what's visible and mark the cut with '[…]'.",
150
+ "- No metaphor anywhere. 'Speech-bubble shape' not 'a thought floating above her head'."
151
+ ],
152
+ "audhd": [
153
+ "Reader preferences (AuDHD) — describe_image:",
154
+ "- INVARIANT: Always populate `content_translation` with one entry per logical item in the image (numbered points, bullets, framework slots, chart series, document sections). Use null ONLY if the image is a decorative avatar/logo/icon with no readable instructional content.",
155
+ "- `content_translation`: cap entries at {max_chunk_size}; each facet is verb-led, literal, 8 words or fewer, no idioms. Quote source labels verbatim.",
156
+ "- 'description': 1 sentence (metadata). Lead with the literal subject in 4-7 words, then the inferred purpose.",
157
+ "- 'key_elements': cap at {max_chunk_size}. Literal nouns + adjectives, visual-prominence order, no meaning-inference.",
158
+ "- 'inferred_purpose': noun phrase, not sentence. When unambiguous, commit ('Promotional slide'). When not, write 'I can't tell from the image alone'.",
159
+ "- Banned: 'perhaps', 'appears to', 'kind of', 'sort of', 'looks like'. Use 'shows' or 'contains'; flag genuine uncertainty in 'accessibility_notes'.",
160
+ "- 'transcribed_text': exact verbatim. Mark obscured portions with '[…]'."
161
+ ],
162
+ "ocd": [
163
+ "Reader preferences (OCD) — describe_image:",
164
+ "- INVARIANT: Always populate `content_translation` with one entry per logical item in the image (numbered points, bullets, framework slots, chart series, document sections). Use null ONLY if the image is a decorative avatar/logo/icon with no readable instructional content.",
165
+ "- `content_translation`: in 'action' and 'rule' facets, avoid 'must', 'urgent', 'critical', 'don't forget' unless the source uses those exact words. Prefer 'consider', 'you may want to', 'when ready'. Don't amplify pressure beyond the source.",
166
+ "- In 'description' and 'inferred_purpose', don't say a slide is 'missing' something or 'incomplete' unless the image literally shows that. Slides are point-in-time snapshots; absence of an element is not a defect.",
167
+ "- Avoid 'error', 'wrong', 'broken', 'mistake', 'should' in 'description'.",
168
+ "- 'accessibility_notes': low-pressure framing. 'Consider adding alt-text describing X' not 'Alt-text is missing and must be added'.",
169
+ "- If text is obscured in 'transcribed_text', describe what's visible without amplifying the concern."
170
+ ],
171
+ "dyslexia": [
172
+ "Reader preferences (dyslexia) — describe_image:",
173
+ "- INVARIANT: Always populate `content_translation` with one entry per logical item in the image (numbered points, bullets, framework slots, chart series, document sections). Use null ONLY if the image is a decorative avatar/logo/icon with no readable instructional content.",
174
+ "- `content_translation`: each facet text max 15 words, one idea per facet, common words ('use' not 'utilise'), no semicolons. Cap entries at {max_chunk_size}.",
175
+ "- 'description': 1 sentence, max 15 words. Replace 'depicted' with 'shown', 'inferred' with 'guessed', 'transcribed' with 'copied'.",
176
+ "- 'key_elements': simple nouns, cap at {max_chunk_size}. 'Logo' not 'Brand identity element'. 'Person' not 'Individual subject'.",
177
+ "- 'inferred_purpose': 5 words or fewer. 'Promotional banner', 'Bar chart', 'UI screenshot'.",
178
+ "- One idea per sentence everywhere. No semicolons. No em-dashes inside sentences.",
179
+ "- 'accessibility_notes': plain words. 'Hard to read' not 'low legibility'."
180
+ ],
181
+ "dyspraxia": [
182
+ "Reader preferences (dyspraxia) — describe_image:",
183
+ "- INVARIANT: Always populate `content_translation` with one entry per logical item in the image (numbered points, bullets, framework slots, chart series, document sections). Use null ONLY if the image is a decorative avatar/logo/icon with no readable instructional content.",
184
+ "- `content_translation`: order entries top-to-bottom / left-to-right as they appear in the image, not by importance. Use absolute dates inside facets when the source uses any date ('Wednesday 28 May', not 'next week'). Never write 'as above' inside a facet — name the prior label.",
185
+ "- For sequenced visuals (numbered steps, flowcharts, timelines), describe in the order they appear in the image. Don't reorder by importance.",
186
+ "- 'key_elements': ordered top-to-bottom / left-to-right, not by prominence. Cap at {max_chunk_size}.",
187
+ "- 'description': name spatial relationships explicitly ('logo top-left, headline centre, CTA button bottom-right') rather than 'the above'.",
188
+ "- 'transcribed_text': preserve original order even if visually scattered; use line breaks between disjoint blocks."
189
+ ]
190
+ },
191
+ "translate_incoming": {
192
+ "adhd": [
193
+ "Reader preferences (ADHD) — translate_incoming:",
194
+ "- 'explicit_ask': 1 sentence, lead with the verb ('Send the spec by Friday' not 'The sender would like…').",
195
+ "- 'likely_subtext': cap at {max_chunk_size} items, highest-confidence first.",
196
+ "- 'recommended_next_action.action': imperative verb-phrase, 6 words or fewer. 'Reply with the spec link' not 'It might be worth replying'.",
197
+ "- 'recommended_next_action.reason': 1 sentence. No throat-clearing.",
198
+ "- 'recommended_next_action.draft_reply': if drafted, keep it to 1-2 short sentences."
199
+ ],
200
+ "asd": [
201
+ "Reader preferences (autism) — translate_incoming:",
202
+ "- 'likely_subtext[].text': state subtext as commitments when evidence supports it ('The sender wants X by Friday'). Use guesses ('they might want X') ONLY when the message is genuinely ambiguous.",
203
+ "- Decode any idiom in the source verbatim inside 'likely_subtext' (e.g. source 'circle back' -> subtext 'They will reply later'; source 'ping you' -> 'They will send a message').",
204
+ "- 'ambiguity.spans[]': when a sentence depends on tone of voice or facial expression text cannot carry, mark it ambiguous with reason 'text alone cannot disambiguate tone'.",
205
+ "- 'recommended_next_action.draft_reply': no idioms ('touch base', 'circle back', 'loop in', 'hop on a call', 'reach out'). Use literal verbs.",
206
+ "- 'recommended_next_action.reason': quote the source verbatim in parentheses when paraphrasing the ask."
207
+ ],
208
+ "audhd": [
209
+ "Reader preferences (AuDHD) — translate_incoming:",
210
+ "- 'explicit_ask': 1 sentence, lead with the verb. No throat-clearing.",
211
+ "- 'likely_subtext': cap at {max_chunk_size}; state as commitments when text supports it, as guesses only when genuinely ambiguous.",
212
+ "- Decode source idioms verbatim in 'likely_subtext' ('ping you' -> 'send you a message').",
213
+ "- 'recommended_next_action.action': imperative verb-phrase, 6 words or fewer, no idioms.",
214
+ "- 'recommended_next_action.draft_reply': 1-2 short sentences, literal verbs only.",
215
+ "- 'ambiguity.spans[]': flag tone-dependent sentences with reason 'text alone cannot disambiguate tone'."
216
+ ],
217
+ "ocd": [
218
+ "Reader preferences (OCD) — translate_incoming:",
219
+ "- 'recommended_next_action.action': low-pressure phrasing. Avoid 'urgent', 'must', 'immediately', 'ASAP', 'critical'. Prefer 'when you have a minute', 'consider', 'you may want to'.",
220
+ "- 'recommended_next_action.reason': do not amplify time pressure. If the source said 'soon', don't paraphrase as 'urgently'.",
221
+ "- 'ambiguity.detected': only flag ambiguity that's actually in the source. Don't invent ambiguity to be thorough.",
222
+ "- 'likely_subtext': if the message has no actionable issue, return an empty array and say so plainly in 'recommended_next_action.reason' ('nothing here needs an answer today').",
223
+ "- 'draft_reply': never say the reader's prior draft was 'wrong'. Use 'reads as' or 'may land as'."
224
+ ],
225
+ "dyslexia": [
226
+ "Reader preferences (dyslexia) — translate_incoming:",
227
+ "- 'explicit_ask': max 15 words. Plain language. 'They want X' not 'The sender is requesting X'.",
228
+ "- 'likely_subtext[].text': 1 sentence each, max 15 words. Cap list at {max_chunk_size}.",
229
+ "- Replace 'paraphrased' with 'said plainly', 'ambiguous' with 'unclear', 'verbatim' with 'word-for-word', 'subtext' with 'what they really mean'.",
230
+ "- 'recommended_next_action.reason': 1 short sentence. No semicolons. No em-dashes inside sentences.",
231
+ "- 'recommended_next_action.draft_reply': short sentences. Common words ('use' not 'utilise', 'about' not 'regarding')."
232
+ ],
233
+ "dyspraxia": [
234
+ "Reader preferences (dyspraxia) — translate_incoming:",
235
+ "- Use absolute dates in 'explicit_ask' and 'draft_reply' ('Wednesday 28 May', 'by end of this week') not relative ones ('next sprint', 'soon').",
236
+ "- 'likely_subtext': cap at {max_chunk_size}; if subtext references a prior thread message, name it explicitly rather than 'the above'.",
237
+ "- 'recommended_next_action.action': single-step where possible. If multi-step, number the steps inside 'draft_reply', not inside 'action'.",
238
+ "- Group related subtext items together — don't interleave 'they want X' with 'they're worried about Y'."
239
+ ]
240
+ },
241
+ "check_tone": {
242
+ "adhd": [
243
+ "Reader preferences (ADHD) — check_tone:",
244
+ "- 'flagged_phrases': cap at {max_chunk_size}, highest-impact first. Don't pad the list.",
245
+ "- 'flagged_phrases[].reason': 1 short sentence per entry.",
246
+ "- 'flagged_phrases[].suggestion': a concrete replacement, not advice. 'Replace with: \"by Friday\"' not 'Consider being more specific'.",
247
+ "- 'suggested_rewrite_hint': 1-2 sentences, verb-led. 'Tighten the opener, drop the hedges' not 'You might want to consider tightening…'."
248
+ ],
249
+ "asd": [
250
+ "Reader preferences (autism) — check_tone:",
251
+ "- 'axes': state the score literally — don't soften with 'somewhat' or 'a bit'. 0.2 means 'low'; say so.",
252
+ "- 'baseline_delta': when prior messages aren't available, return null rather than guessing a baseline.",
253
+ "- 'flagged_phrases[].reason': explain WHY the phrase reads as it does ('reads as terse because it has no greeting and uses imperative mood'), not just 'sounds blunt'.",
254
+ "- 'suggested_rewrite_hint': literal, no idioms ('touch base', 'soften the ask', 'warm up'). Use concrete edits ('add a greeting; replace \"send it\" with \"could you send it\"').",
255
+ "- 'flagged_phrases[].suggestion': exact replacement string, not a description of the change."
256
+ ],
257
+ "audhd": [
258
+ "Reader preferences (AuDHD) — check_tone:",
259
+ "- 'flagged_phrases': cap at {max_chunk_size}, highest-impact first. Each 'suggestion' is a concrete replacement string, not advice.",
260
+ "- 'flagged_phrases[].reason': 1 sentence; name the mechanism ('imperative mood, no greeting') not just the impression ('blunt').",
261
+ "- 'suggested_rewrite_hint': verb-led, literal, no idioms. 'Add a greeting; soften the ask by changing \"send it\" to \"could you send it\".'",
262
+ "- 'axes': state scores literally — don't hedge with 'somewhat'.",
263
+ "- 'baseline_delta': null when no prior baseline; don't guess."
264
+ ],
265
+ "ocd": [
266
+ "Reader preferences (OCD) — check_tone:",
267
+ "- 'flagged_phrases[].reason': avoid 'wrong', 'mistake', 'error'. Use 'reads as' or 'may land as'.",
268
+ "- 'suggested_rewrite_hint': low-pressure framing. 'You may want to add a greeting' not 'You must add a greeting'.",
269
+ "- Do not flag a phrase as a problem if the only issue is that it could theoretically be misread. Flag actual tone issues, not hypothetical ones.",
270
+ "- 'axes.urgency': if the source genuinely is urgent (deadline in source text), don't downgrade the score to seem 'calmer'. Report the tone the message actually has."
271
+ ],
272
+ "dyslexia": [
273
+ "Reader preferences (dyslexia) — check_tone:",
274
+ "- 'flagged_phrases': cap at {max_chunk_size}. Each 'reason' max 15 words.",
275
+ "- 'suggested_rewrite_hint': 1-2 short sentences. Plain words ('softer' not 'less assertive', 'shorter' not 'more concise').",
276
+ "- One idea per sentence in every prose field. No semicolons.",
277
+ "- 'flagged_phrases[].suggestion': a concrete replacement string, short."
278
+ ],
279
+ "dyspraxia": [
280
+ "Reader preferences (dyspraxia) — check_tone:",
281
+ "- 'flagged_phrases': {max_chunk_size} max, ordered as they appear in the source text (top to bottom) — not by severity.",
282
+ "- 'suggested_rewrite_hint': if multi-step, write as a single continuous instruction rather than a numbered sequence.",
283
+ "- Don't reference 'the above' or 'as mentioned'; name the specific phrase ('the opener \"hey\"', not 'the first issue')."
284
+ ]
285
+ },
286
+ "rewrite_outgoing": {
287
+ "adhd": [
288
+ "Reader preferences (ADHD) — rewrite_outgoing:",
289
+ "- 'rewritten': keep length within ~20% of the source. Don't pad to sound 'professional'.",
290
+ "- 'diff_summary.structural_changes': cap at {max_chunk_size}, highest-impact first.",
291
+ "- 'diff_summary.tone_shift': 1 short sentence describing the shift verb-first ('Softened the ask; kept the deadline').",
292
+ "- 'diff_summary.warnings': only include warnings the reader actually needs to act on. Skip 'minor style changes'."
293
+ ],
294
+ "asd": [
295
+ "Reader preferences (autism) — rewrite_outgoing:",
296
+ "- 'rewritten': preserve the literal ask. If the source says 'send the report by Friday', the rewrite still says that explicitly — don't bury it under softening.",
297
+ "- 'rewritten': no idioms introduced. Banned in the rewrite: 'touch base', 'circle back', 'loop in', 'hop on', 'reach out', 'moving forward', 'low-hanging fruit'.",
298
+ "- 'preserved_terms': include any technical term, name, or quoted phrase from the source that has a precise meaning.",
299
+ "- 'diff_summary.tone_shift': name the mechanism ('added a greeting; replaced imperative with question form'), not the impression ('made it warmer').",
300
+ "- 'diff_summary.warnings': flag if softening could obscure the deadline or quantity in the source."
301
+ ],
302
+ "audhd": [
303
+ "Reader preferences (AuDHD) — rewrite_outgoing:",
304
+ "- 'rewritten': keep within ~20% of source length; preserve the literal ask verbatim; no introduced idioms.",
305
+ "- 'diff_summary.structural_changes': cap at {max_chunk_size}; name mechanisms not impressions.",
306
+ "- 'diff_summary.tone_shift': 1 sentence, verb-led. 'Added greeting, replaced imperative with question form.'",
307
+ "- 'preserved_terms': include technical terms, names, and quoted source phrases.",
308
+ "- 'diff_summary.warnings': flag if softening would obscure the deadline or quantity."
309
+ ],
310
+ "ocd": [
311
+ "Reader preferences (OCD) — rewrite_outgoing:",
312
+ "- 'rewritten': avoid 'urgent', 'must', 'ASAP', 'critical', 'don't forget' unless the source uses them. Don't amplify pressure beyond the source.",
313
+ "- 'diff_summary.warnings': don't warn that the source was 'wrong' or 'aggressive'. Use 'the original could land as terse' instead.",
314
+ "- 'diff_summary.tone_shift': avoid 'fixed', 'corrected', 'cleaned up'. Use 'shifted toward' or 'reduced'.",
315
+ "- Don't add hedging the source didn't have unless the target_register asks for it."
316
+ ],
317
+ "dyslexia": [
318
+ "Reader preferences (dyslexia) — rewrite_outgoing:",
319
+ "- 'rewritten': short sentences, max 15 words each. Common words ('use' not 'utilise', 'help' not 'facilitate').",
320
+ "- 'diff_summary.structural_changes': cap at {max_chunk_size}; each item 1 short phrase, not a sentence.",
321
+ "- 'diff_summary.tone_shift': 1 short sentence. Plain words.",
322
+ "- Break compound sentences into two during the rewrite. No semicolons in 'rewritten'."
323
+ ],
324
+ "dyspraxia": [
325
+ "Reader preferences (dyspraxia) — rewrite_outgoing:",
326
+ "- 'rewritten': use absolute dates ('Wednesday 28 May'), not relative ones ('next week'), even if the source used relative.",
327
+ "- 'diff_summary.structural_changes': {max_chunk_size} max, ordered by where they appear in the rewrite (top to bottom).",
328
+ "- If the source asks for a sequence of actions, keep the sequence in source order in the rewrite; don't reorder for 'flow'.",
329
+ "- 'diff_summary.warnings': flag if the rewrite introduces a forward-reference ('as mentioned above') that the source didn't have."
330
+ ]
331
+ },
332
+ "brief_meeting": {
333
+ "adhd": [
334
+ "Reader preferences (ADHD) — brief_meeting:",
335
+ "- INVARIANT: Always populate `content_translation` with one entry per ask, decision, or actionable ambiguous item in the transcript. Use null ONLY if the meeting is chat-only and all four legacy sections (my_asks/others_asks/decisions/ambiguous_items) are empty.",
336
+ "- `content_translation`: cap entries at {max_chunk_size}; each facet verb-led and 8 words or fewer. Label each entry as 'my_asks[i]' / 'decisions[i]' so the reader can jump back to the verbatim quote.",
337
+ "- 'my_asks': cap at {max_chunk_size}, highest-priority first. Each ask is verb-led ('Send the spec to Bob') in 8 words or fewer.",
338
+ "- 'others_asks': same cap and shape.",
339
+ "- 'decisions[]': 1 short sentence each. State the decision, not the discussion.",
340
+ "- 'ambiguous_items[]': include ONLY items the reader needs to chase. Skip 'minor topics raised'."
341
+ ],
342
+ "asd": [
343
+ "Reader preferences (autism) — brief_meeting:",
344
+ "- INVARIANT: Always populate `content_translation` with one entry per ask, decision, or actionable ambiguous item in the transcript. Use null ONLY if the meeting is chat-only and all four legacy sections (my_asks/others_asks/decisions/ambiguous_items) are empty.",
345
+ "- `content_translation`: each facet is a literal commitment. Decode source idioms inside facets ('circle back' → 'reply later'). Quote the source 'asker' speaker in the 'label' field.",
346
+ "- 'my_asks[].text' and 'others_asks[].text': quote the original speaker verbatim in parentheses when paraphrasing ('Send the spec (\"can you ping me the spec by Fri?\")').",
347
+ "- 'decisions[]': state the decision and the speaker who made it. Don't infer consensus that wasn't stated.",
348
+ "- 'ambiguous_items[]': flag any item where the transcript depends on tone or shared context not in the text. Reason 'verbal agreement implied but not stated' is a legitimate ambiguity.",
349
+ "- No idioms in any text field ('touch base', 'circle back', 'loop in')."
350
+ ],
351
+ "audhd": [
352
+ "Reader preferences (AuDHD) — brief_meeting:",
353
+ "- INVARIANT: Always populate `content_translation` with one entry per ask, decision, or actionable ambiguous item in the transcript. Use null ONLY if the meeting is chat-only and all four legacy sections (my_asks/others_asks/decisions/ambiguous_items) are empty.",
354
+ "- `content_translation`: cap at {max_chunk_size}; verb-led, literal, 8 words or fewer, no idioms. Label each entry as 'my_asks[i]' / 'decisions[i]'.",
355
+ "- 'my_asks' / 'others_asks': cap at {max_chunk_size}, verb-led, 8 words or fewer. Quote source verbatim in parentheses when paraphrasing.",
356
+ "- 'decisions[]': 1 sentence each, name the speaker; don't infer unstated consensus.",
357
+ "- 'ambiguous_items[]': flag tone-dependent items with reason 'verbal agreement implied but not stated'. Skip 'minor topics raised'.",
358
+ "- No idioms anywhere."
359
+ ],
360
+ "ocd": [
361
+ "Reader preferences (OCD) — brief_meeting:",
362
+ "- INVARIANT: Always populate `content_translation` with one entry per ask, decision, or actionable ambiguous item in the transcript. Use null ONLY if the meeting is chat-only and all four legacy sections (my_asks/others_asks/decisions/ambiguous_items) are empty.",
363
+ "- `content_translation`: in 'action' facets, avoid 'must', 'urgent', 'ASAP', 'critical' unless the source used them. Prefer 'when you have a minute'. Don't amplify pressure beyond the transcript.",
364
+ "- 'my_asks[]': don't add asks that weren't actually assigned to the reader. If the transcript is ambiguous about who owns an action, put it in 'ambiguous_items[]' instead.",
365
+ "- 'ambiguous_items[]': distinguish 'unclear who owns this' from 'this needs follow-up'. Reason field should name which kind of ambiguity.",
366
+ "- No 'must', 'urgent', 'critical' in 'my_asks[].text' unless the speaker used those words.",
367
+ "- 'decisions[]': don't flag a decision as 'final' unless the transcript explicitly closes it."
368
+ ],
369
+ "dyslexia": [
370
+ "Reader preferences (dyslexia) — brief_meeting:",
371
+ "- INVARIANT: Always populate `content_translation` with one entry per ask, decision, or actionable ambiguous item in the transcript. Use null ONLY if the meeting is chat-only and all four legacy sections (my_asks/others_asks/decisions/ambiguous_items) are empty.",
372
+ "- `content_translation`: each facet text max 15 words, one idea per facet, common words, no semicolons. Cap entries at {max_chunk_size}.",
373
+ "- All list fields: cap at {max_chunk_size}, each item max 15 words.",
374
+ "- Plain language. 'Bob will send the spec' not 'Bob will action distribution of the specification'.",
375
+ "- 'decisions[]': one decision per item. Don't combine two decisions into one sentence with 'and'.",
376
+ "- 'ambiguous_items[].reason': 1 short sentence; no semicolons."
377
+ ],
378
+ "dyspraxia": [
379
+ "Reader preferences (dyspraxia) — brief_meeting:",
380
+ "- INVARIANT: Always populate `content_translation` with one entry per ask, decision, or actionable ambiguous item in the transcript. Use null ONLY if the meeting is chat-only and all four legacy sections (my_asks/others_asks/decisions/ambiguous_items) are empty.",
381
+ "- `content_translation`: order entries by transcript appearance, not priority. Absolute dates inside facets. Never 'as above' — name the prior label.",
382
+ "- 'my_asks' / 'others_asks': ordered by when they appear in the transcript, not by priority. Cap at {max_chunk_size}.",
383
+ "- Use absolute dates in 'text' fields when the transcript implies a deadline ('Wednesday 28 May' rather than 'next week').",
384
+ "- 'decisions[]': name the topic explicitly each time. Don't write 'as discussed above' or 'the prior point'.",
385
+ "- Group related items together — don't interleave asks from different topics."
386
+ ]
387
+ }
388
+ }
389
+ }
@@ -0,0 +1,5 @@
1
+ export declare const version = "0.2.0";
2
+ export type { Profile, ProfileIdentity, ProfilePreferences, ProfileChronometric, ProfileGuardrails, ProfilePrivacy, WeekdayOverride, ProtectedWindow, Neurotype, OutputFormat, ReadingFontHint, Motion, LineHeightHint, SessionOverlapPolicy, CalendarPhase, SycophancyCheck, Embeddings, Telemetry, Weekday, } from "./profile.js";
3
+ export { assembleNeurotypeAddendum, neurotypeAddendaV1, } from "./neurotype-addenda.js";
4
+ export type { NeurotypeAddendaArtifact, AddendumBlock, AddendumFusion, AddendumFraming, AddendumOutputFormat, AddendumSingleBlock, AssembleNeurotypeAddendumOptions, } from "./neurotype-addenda.js";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,OAAO,UAAU,CAAC;AAE/B,YAAY,EACV,OAAO,EACP,eAAe,EACf,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,EACjB,cAAc,EACd,eAAe,EACf,eAAe,EACf,SAAS,EACT,YAAY,EACZ,eAAe,EACf,MAAM,EACN,cAAc,EACd,oBAAoB,EACpB,aAAa,EACb,eAAe,EACf,UAAU,EACV,SAAS,EACT,OAAO,GACR,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,yBAAyB,EACzB,kBAAkB,GACnB,MAAM,wBAAwB,CAAC;AAEhC,YAAY,EACV,wBAAwB,EACxB,aAAa,EACb,cAAc,EACd,eAAe,EACf,oBAAoB,EACpB,mBAAmB,EACnB,gCAAgC,GACjC,MAAM,wBAAwB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ /*
2
+ * SPDX-License-Identifier: AGPL-3.0-or-later
3
+ * Copyright (c) 2026 NeuroDock contributors.
4
+ */
5
+ export const version = "0.2.0";
6
+ export { assembleNeurotypeAddendum, neurotypeAddendaV1, } from "./neurotype-addenda.js";
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC;AAwB/B,OAAO,EACL,yBAAyB,EACzB,kBAAkB,GACnB,MAAM,wBAAwB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,29 @@
1
+ /*
2
+ * SPDX-License-Identifier: AGPL-3.0-or-later
3
+ * Copyright (c) 2026 NeuroDock contributors.
4
+ */
5
+ import { readFileSync } from "node:fs";
6
+ import { fileURLToPath } from "node:url";
7
+ import { dirname, resolve } from "node:path";
8
+ import { describe, expect, test } from "vitest";
9
+ import { version } from "./index";
10
+ const here = dirname(fileURLToPath(import.meta.url));
11
+ function readPackageVersion() {
12
+ const raw = readFileSync(resolve(here, "..", "package.json"), "utf8");
13
+ return JSON.parse(raw).version;
14
+ }
15
+ describe("@neurodock/core", () => {
16
+ test("exports a version constant", () => {
17
+ expect(version).toMatch(/^\d+\.\d+\.\d+/);
18
+ });
19
+ // Version-drift guard: `changeset version` bumps package.json but cannot touch
20
+ // the hand-written `version` constant in src/index.ts. This asserts the two
21
+ // stay joined, so a release that forgets to bump the constant fails CI here
22
+ // instead of shipping a mismatch. (We assert against package.json read at
23
+ // runtime rather than deriving the constant from it, to keep src/index.ts a
24
+ // plain `tsc` build with no JSON import escaping rootDir.)
25
+ test("exported version matches package.json", () => {
26
+ expect(version).toBe(readPackageVersion());
27
+ });
28
+ });
29
+ //# sourceMappingURL=index.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.test.js","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAChD,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAErD,SAAS,kBAAkB;IACzB,MAAM,GAAG,GAAG,YAAY,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAAC;IACtE,OAAQ,IAAI,CAAC,KAAK,CAAC,GAAG,CAAyB,CAAC,OAAO,CAAC;AAC1D,CAAC;AAED,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;IAC/B,IAAI,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACtC,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,+EAA+E;IAC/E,4EAA4E;IAC5E,4EAA4E;IAC5E,0EAA0E;IAC1E,4EAA4E;IAC5E,2DAA2D;IAC3D,IAAI,CAAC,uCAAuC,EAAE,GAAG,EAAE;QACjD,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,91 @@
1
+ /** A prose block: ordered lines joined with the framing line separator. */
2
+ export type AddendumBlock = readonly string[];
3
+ /** The fusion (AuDHD substitution) rule. */
4
+ export interface AddendumFusion {
5
+ readonly description?: string;
6
+ readonly result: string;
7
+ readonly any_of?: readonly string[];
8
+ readonly all_of?: readonly string[];
9
+ readonly remove?: readonly string[];
10
+ }
11
+ /** Wrapper, header, footer, separators, and conflict-footer copy. */
12
+ export interface AddendumFraming {
13
+ readonly wrapper_prefix: string;
14
+ readonly wrapper_suffix: string;
15
+ readonly section_separator: string;
16
+ readonly block_line_separator: string;
17
+ readonly header: AddendumBlock;
18
+ readonly footer: AddendumBlock;
19
+ readonly conflict_footer_min_neurotypes: number;
20
+ readonly conflict_footer: string;
21
+ }
22
+ /** Per-output-format guidance. */
23
+ export interface AddendumOutputFormat {
24
+ readonly prefix: string;
25
+ readonly separator: string;
26
+ readonly descriptions: Readonly<Record<string, string>>;
27
+ readonly default: string;
28
+ }
29
+ /** A single-block section (voice-input / tourette / other). */
30
+ export interface AddendumSingleBlock {
31
+ readonly block: AddendumBlock;
32
+ }
33
+ /** The full artifact shape. Mirrors `schemas/neurotype-addenda.schema.json`. */
34
+ export interface NeurotypeAddendaArtifact {
35
+ readonly artifact_version: string;
36
+ readonly description?: string;
37
+ readonly tokens?: readonly string[];
38
+ readonly fusion: AddendumFusion;
39
+ readonly priority: readonly string[];
40
+ readonly framing: AddendumFraming;
41
+ readonly output_format: AddendumOutputFormat;
42
+ readonly voice_input: AddendumSingleBlock;
43
+ readonly tourette: AddendumSingleBlock;
44
+ readonly other: AddendumSingleBlock;
45
+ readonly generic: Readonly<Record<string, AddendumBlock>>;
46
+ readonly tools: Readonly<Record<string, Readonly<Record<string, AddendumBlock>>>>;
47
+ }
48
+ /**
49
+ * The shipped v1 artifact, typed. Re-exported so consumers (the browser
50
+ * extension, the mcp-translation server) bundle the canonical content
51
+ * through `@neurodock/core` without re-reading the JSON file themselves.
52
+ */
53
+ export declare const neurotypeAddendaV1: NeurotypeAddendaArtifact;
54
+ /** Inputs to {@link assembleNeurotypeAddendum}. */
55
+ export interface AssembleNeurotypeAddendumOptions {
56
+ /**
57
+ * The tool being shaped (e.g. `"translate_incoming"`). When provided and a
58
+ * concrete per-tool block exists for a neurotype, that block is used;
59
+ * otherwise the generic fallback block is used. When omitted, every
60
+ * neurotype falls back to its generic block.
61
+ */
62
+ readonly tool?: string;
63
+ /** The reader's self-identified neurotypes. */
64
+ readonly neurotypes: readonly string[];
65
+ /**
66
+ * How the reader wants prose structured. Defaults to the artifact's
67
+ * declared default when omitted.
68
+ */
69
+ readonly outputFormat?: string;
70
+ /** Per-list item cap interpolated into `{max_chunk_size}` tokens. */
71
+ readonly maxChunkSize: number;
72
+ /** R5 cross-cutting voice-input hint. */
73
+ readonly voiceInputPreferred?: boolean;
74
+ /** Free-form reader notes interpolated into the `{notes}` token. */
75
+ readonly additionalNotes?: string | null;
76
+ }
77
+ /**
78
+ * Assemble the per-neurotype prompt addendum from the artifact.
79
+ *
80
+ * Pure function: deterministic, no I/O, no mutation of inputs. Reproduces
81
+ * the assembly the NeuroDock browser extension shipped (fusion -> priority
82
+ * order -> per-tool blocks with generic fallback -> tourette/other specials
83
+ * -> voice-input cross-cutting block -> conflict footer -> token
84
+ * interpolation) so per-neurotype shaping is byte-identical across surfaces.
85
+ *
86
+ * Returns "" when there are no effective neurotypes, no notes, a default
87
+ * output format, AND no voice-input hint — i.e. the all-default case stays
88
+ * byte-identical to an un-shaped prompt.
89
+ */
90
+ export declare function assembleNeurotypeAddendum(artifact: NeurotypeAddendaArtifact, options: AssembleNeurotypeAddendumOptions): string;
91
+ //# sourceMappingURL=neurotype-addenda.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"neurotype-addenda.d.ts","sourceRoot":"","sources":["../src/neurotype-addenda.ts"],"names":[],"mappings":"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"}