@neurodock/cli 0.8.1 → 0.9.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/CHANGELOG.md +43 -0
- package/README.md +45 -11
- package/dist/assets/hooks/proactive_guardrail.py +11 -23
- package/dist/assets/schemas/neurotype-addenda.schema.json +167 -0
- package/dist/assets/schemas/profile.example.yaml +9 -0
- package/dist/assets/schemas/profile.schema.json +116 -0
- package/dist/assets/skills/adhd-daily-planner/SKILL.md +131 -0
- package/dist/assets/skills/asd-meeting-translator/SKILL.md +177 -0
- package/dist/assets/skills/audhd-context-recovery/SKILL.md +93 -0
- package/dist/assets/skills/dyspraxia-task-pacer/SKILL.md +174 -0
- package/dist/assets/skills/hyperfocus-formatter/SKILL.md +89 -0
- package/dist/assets/skills/ocd-decision-finalizer/SKILL.md +109 -0
- package/dist/assets/skills/visual-organizer/SKILL.md +94 -0
- package/dist/commands/host.d.ts.map +1 -1
- package/dist/commands/host.js +4 -6
- package/dist/commands/host.js.map +1 -1
- package/dist/commands/install-all.d.ts +27 -0
- package/dist/commands/install-all.d.ts.map +1 -1
- package/dist/commands/install-all.js +53 -1
- package/dist/commands/install-all.js.map +1 -1
- package/dist/commands/install-skills.d.ts +26 -0
- package/dist/commands/install-skills.d.ts.map +1 -0
- package/dist/commands/install-skills.js +164 -0
- package/dist/commands/install-skills.js.map +1 -0
- package/dist/commands/setup.d.ts +12 -0
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +2 -0
- package/dist/commands/setup.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/skills.d.ts +40 -0
- package/dist/lib/skills.d.ts.map +1 -0
- package/dist/lib/skills.js +115 -0
- package/dist/lib/skills.js.map +1 -0
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,48 @@
|
|
|
1
1
|
# @neurodock/cli changelog
|
|
2
2
|
|
|
3
|
+
## 0.9.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 1c76c2c: add `neurodock install-skills` and bundle the per-neurotype skills into the cli
|
|
8
|
+
|
|
9
|
+
the cli now ships the six per-neurotype skills (their `SKILL.md` files,
|
|
10
|
+
generated into `dist/assets/skills/` at build time from `packages/skills/`).
|
|
11
|
+
the new `neurodock install-skills` command copies them into the client's
|
|
12
|
+
personal skills directory (`~/.claude/skills/neurodock-<name>/` for claude code
|
|
13
|
+
and claude desktop; cursor is skipped). it supports `--client`, `--dry-run`,
|
|
14
|
+
and `--yes`, and is idempotent.
|
|
15
|
+
|
|
16
|
+
`install-all`, `setup`, and `update` now install the skills as part of the
|
|
17
|
+
one-command happy path; opt out with `--no-skills` (mirrors `--no-native-host`).
|
|
18
|
+
the skills step is best-effort — a failure warns but does not fail the command.
|
|
19
|
+
|
|
20
|
+
## 0.8.2
|
|
21
|
+
|
|
22
|
+
### Patch Changes
|
|
23
|
+
|
|
24
|
+
- a7c3f01: fix(native-host): register the real published extension ids so the extension can connect
|
|
25
|
+
|
|
26
|
+
The native-messaging host was registered with the literal placeholder
|
|
27
|
+
`__NEURODOCK_EXTENSION_ID__`, which was never substituted — so the host
|
|
28
|
+
manifest's `allowed_origins` matched no extension and the browser
|
|
29
|
+
extension always showed "Not connected yet" even after a successful
|
|
30
|
+
`setup`/`install-all`.
|
|
31
|
+
|
|
32
|
+
- The published Chrome Web Store id (`lcdaiekokkgniiknejddojkfkoiinopo`)
|
|
33
|
+
and the Firefox gecko id (`neurodock-extension@neurodock.org`) are now
|
|
34
|
+
the registered defaults (`PUBLISHED_EXTENSION_IDS` /
|
|
35
|
+
`withDefaultExtensionIds` in `@neurodock/native-host`), so a
|
|
36
|
+
store-installed extension connects out of the box.
|
|
37
|
+
- `neurodock setup` and `neurodock install-all` gained a repeatable
|
|
38
|
+
`--extension-id <id>` flag, threaded down to the host installer, so a
|
|
39
|
+
locally-loaded unpacked build (whose id differs from the published one)
|
|
40
|
+
can be allowed in one command. Provided ids are unioned with the
|
|
41
|
+
published defaults and deduped.
|
|
42
|
+
|
|
43
|
+
- Updated dependencies [a7c3f01]
|
|
44
|
+
- @neurodock/native-host@0.2.1
|
|
45
|
+
|
|
3
46
|
## 0.8.1
|
|
4
47
|
|
|
5
48
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
The `neurodock` installer and diagnostic CLI for [NeuroDock](https://neurodock.org/) — a local-first cognitive substrate for neurodivergent professionals.
|
|
4
4
|
|
|
5
|
-
Status: **v0.
|
|
5
|
+
Status: **v0.8.2**.
|
|
6
6
|
|
|
7
7
|
## Quickstart
|
|
8
8
|
|
|
@@ -34,8 +34,9 @@ install, `install-all` runs the `pip install` step automatically.
|
|
|
34
34
|
|
|
35
35
|
| Command | What it does |
|
|
36
36
|
| ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
37
|
-
| `neurodock install-all` | One-command first-time install: pip-install the six servers, wire every detected client, copy the starter profile.
|
|
37
|
+
| `neurodock install-all` | One-command first-time install: pip-install the six servers, wire every detected client, copy the starter profile, install the per-neurotype skills. |
|
|
38
38
|
| `neurodock init` | Install MCP servers into Claude Desktop / Claude Code / Cursor (the wiring half of `install-all`). |
|
|
39
|
+
| `neurodock install-skills` | Copy the per-neurotype skills into your client's personal skills directory (`~/.claude/skills` for Claude Code / Claude Desktop). Cursor is skipped. |
|
|
39
40
|
| `neurodock doctor` | Diagnose your install — profile validity, client wiring, tool availability. |
|
|
40
41
|
| `neurodock validate` | Schema-validate a profile file (`~/.neurodock/profile.yaml` by default). |
|
|
41
42
|
| `neurodock update` | Upgrade NeuroDock to the latest version — re-installs the six MCP servers via pip/uv and re-wires client configs. |
|
|
@@ -60,16 +61,47 @@ install, `install-all` runs the `pip install` step automatically.
|
|
|
60
61
|
neurodock install-all [--client=claude-desktop|claude-code|cursor|all] \
|
|
61
62
|
[--profile=minimal|example] \
|
|
62
63
|
[--installer=uv|pip|auto] \
|
|
63
|
-
[--skip-install] [--yes] [--dry-run]
|
|
64
|
+
[--skip-install] [--yes] [--dry-run] \
|
|
65
|
+
[--no-native-host] [--no-skills]
|
|
64
66
|
```
|
|
65
67
|
|
|
66
68
|
Single-command first-time install. Prefers `uv` if it is on PATH; falls
|
|
67
69
|
back to `python -m pip`. After install, verifies each server entrypoint
|
|
68
|
-
is on PATH with `<command> --help`,
|
|
69
|
-
|
|
70
|
+
is on PATH with `<command> --help`, delegates to `init` to wire clients,
|
|
71
|
+
registers the optional native-messaging host, and installs the
|
|
72
|
+
per-neurotype skills into `~/.claude/skills`.
|
|
70
73
|
|
|
71
|
-
|
|
72
|
-
|
|
74
|
+
- `--no-native-host` skips registering the native-messaging host.
|
|
75
|
+
- `--no-skills` skips copying the per-neurotype skills.
|
|
76
|
+
|
|
77
|
+
Both steps are best-effort: a failure in either emits a warning but does
|
|
78
|
+
not fail the command (the six MCP servers are the core). Exit codes:
|
|
79
|
+
`0` ok, `1` an entrypoint is missing from PATH after install, `2` init
|
|
80
|
+
failed.
|
|
81
|
+
|
|
82
|
+
### `neurodock install-skills`
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
neurodock install-skills [--client=claude-desktop|claude-code|cursor|all] \
|
|
86
|
+
[--dry-run] [--yes]
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Copies the six per-neurotype skills bundled in the CLI tarball into the
|
|
90
|
+
client's personal skills directory so Claude Code / Claude Desktop
|
|
91
|
+
discover them. Each skill installs as
|
|
92
|
+
`~/.claude/skills/neurodock-<name>/SKILL.md` (namespaced so it is
|
|
93
|
+
collision-free and recognisably NeuroDock's).
|
|
94
|
+
|
|
95
|
+
- Claude Code and Claude Desktop share `~/.claude/skills`; the command
|
|
96
|
+
de-duplicates so the files are written once.
|
|
97
|
+
- Cursor has no skills system today and is skipped with a notice (never
|
|
98
|
+
an error).
|
|
99
|
+
- `--dry-run` prints the planned targets and exits 0 without writing.
|
|
100
|
+
- Idempotent: re-running refreshes each `SKILL.md` in place.
|
|
101
|
+
|
|
102
|
+
This is also run automatically by `install-all` / `setup` / `update`
|
|
103
|
+
unless you pass `--no-skills`. Exit codes: `0` ok, `1` no bundled skills
|
|
104
|
+
were found (a packaging bug).
|
|
73
105
|
|
|
74
106
|
### `neurodock init`
|
|
75
107
|
|
|
@@ -100,14 +132,16 @@ key are skipped unless `--yes` is supplied.
|
|
|
100
132
|
neurodock update [--client=claude-desktop|claude-code|cursor|all] \
|
|
101
133
|
[--profile=minimal|example] \
|
|
102
134
|
[--installer=uv|pip|auto] \
|
|
103
|
-
[--skip-install] [--yes] [--dry-run]
|
|
135
|
+
[--skip-install] [--yes] [--dry-run] \
|
|
136
|
+
[--no-native-host] [--no-skills]
|
|
104
137
|
```
|
|
105
138
|
|
|
106
139
|
One-command upgrade. Same code path as `install-all` — re-runs
|
|
107
140
|
`pip install --upgrade` (or `uv tool install`) for every NeuroDock MCP
|
|
108
|
-
server, re-wires the detected MCP clients,
|
|
109
|
-
|
|
110
|
-
`0` ok, `1` an
|
|
141
|
+
server, re-wires the detected MCP clients, re-registers the optional
|
|
142
|
+
native-messaging host, and refreshes the per-neurotype skills (skip with
|
|
143
|
+
`--no-skills`). Exit codes match `install-all`: `0` ok, `1` an
|
|
144
|
+
entrypoint is missing from PATH, `2` init failed.
|
|
111
145
|
|
|
112
146
|
### `neurodock sync`
|
|
113
147
|
|
|
@@ -174,9 +174,7 @@ def _on_session_start(_payload: dict[str, Any], settings: dict[str, Any]) -> Non
|
|
|
174
174
|
state["tool_count"] = 0
|
|
175
175
|
_save_session(state)
|
|
176
176
|
band = _clock_band(now)
|
|
177
|
-
break_minutes = settings.get(
|
|
178
|
-
"hyperfocus_break_minutes", HYPERFOCUS_BREAK_MINUTES_DEFAULT
|
|
179
|
-
)
|
|
177
|
+
break_minutes = settings.get("hyperfocus_break_minutes", HYPERFOCUS_BREAK_MINUTES_DEFAULT)
|
|
180
178
|
if band in ("deep_night", "late_night"):
|
|
181
179
|
_emit_banner(
|
|
182
180
|
f"NeuroDock: it's {band.replace('_', ' ')} local time. "
|
|
@@ -208,17 +206,13 @@ def _on_pre_tool(payload: dict[str, Any], settings: dict[str, Any]) -> None:
|
|
|
208
206
|
if state["tool_count"] % PRETOOL_CHECK_EVERY_N != 0:
|
|
209
207
|
return
|
|
210
208
|
|
|
211
|
-
break_minutes = settings.get(
|
|
212
|
-
"hyperfocus_break_minutes", HYPERFOCUS_BREAK_MINUTES_DEFAULT
|
|
213
|
-
)
|
|
209
|
+
break_minutes = settings.get("hyperfocus_break_minutes", HYPERFOCUS_BREAK_MINUTES_DEFAULT)
|
|
214
210
|
end_of_day = settings.get("end_of_day_local")
|
|
215
211
|
hyperfocus_banner = _evaluate_hyperfocus(state, break_minutes, end_of_day)
|
|
216
212
|
if hyperfocus_banner:
|
|
217
213
|
_emit_banner(hyperfocus_banner)
|
|
218
214
|
threshold = settings.get("rumination_threshold", RUMINATION_THRESHOLD_DEFAULT)
|
|
219
|
-
window = settings.get(
|
|
220
|
-
"rumination_window_minutes", RUMINATION_WINDOW_MINUTES_DEFAULT
|
|
221
|
-
)
|
|
215
|
+
window = settings.get("rumination_window_minutes", RUMINATION_WINDOW_MINUTES_DEFAULT)
|
|
222
216
|
rumination_banner = _evaluate_rumination(threshold, window)
|
|
223
217
|
if rumination_banner:
|
|
224
218
|
_emit_banner(rumination_banner)
|
|
@@ -518,7 +512,7 @@ def _is_past_end_of_day(now: datetime, end_of_day_local: str | None) -> bool:
|
|
|
518
512
|
hour, minute = int(match.group(1)), int(match.group(2))
|
|
519
513
|
if not (0 <= hour <= 23 and 0 <= minute <= 59):
|
|
520
514
|
return _clock_band(now) in ("late_night", "deep_night")
|
|
521
|
-
# Deep night (00:00
|
|
515
|
+
# Deep night (00:00-05:59) is always "past end of day" regardless of the
|
|
522
516
|
# configured clock-out — nobody sets end_of_day to 03:00 and means it.
|
|
523
517
|
if now.hour < 6:
|
|
524
518
|
return True
|
|
@@ -576,21 +570,15 @@ def _parse_profile_text(text: str) -> dict[str, Any]:
|
|
|
576
570
|
|
|
577
571
|
hyperfocus = _extract_int(text, "hyperfocus_break_minutes")
|
|
578
572
|
if hyperfocus is not None:
|
|
579
|
-
settings["hyperfocus_break_minutes"] = _clamp(
|
|
580
|
-
hyperfocus, *HYPERFOCUS_BREAK_MIN_RANGE
|
|
581
|
-
)
|
|
573
|
+
settings["hyperfocus_break_minutes"] = _clamp(hyperfocus, *HYPERFOCUS_BREAK_MIN_RANGE)
|
|
582
574
|
|
|
583
575
|
threshold = _extract_int(text, "rumination_threshold")
|
|
584
576
|
if threshold is not None:
|
|
585
|
-
settings["rumination_threshold"] = _clamp(
|
|
586
|
-
threshold, *RUMINATION_THRESHOLD_RANGE
|
|
587
|
-
)
|
|
577
|
+
settings["rumination_threshold"] = _clamp(threshold, *RUMINATION_THRESHOLD_RANGE)
|
|
588
578
|
|
|
589
579
|
window = _extract_int(text, "rumination_window_minutes")
|
|
590
580
|
if window is not None:
|
|
591
|
-
settings["rumination_window_minutes"] = _clamp(
|
|
592
|
-
window, *RUMINATION_WINDOW_RANGE
|
|
593
|
-
)
|
|
581
|
+
settings["rumination_window_minutes"] = _clamp(window, *RUMINATION_WINDOW_RANGE)
|
|
594
582
|
|
|
595
583
|
eod = _extract_str(text, "end_of_day_local")
|
|
596
584
|
if eod is not None and re.match(r"^\d{1,2}:\d{2}$", eod):
|
|
@@ -805,15 +793,15 @@ def _self_test() -> int:
|
|
|
805
793
|
|
|
806
794
|
# Profile parsing: extract scalars from a representative profile snippet.
|
|
807
795
|
sample_profile = (
|
|
808
|
-
|
|
796
|
+
'schema_version: "0.1.0"\n'
|
|
809
797
|
"chronometric:\n"
|
|
810
798
|
" # how long before a nudge\n"
|
|
811
799
|
" hyperfocus_break_minutes: 60\n"
|
|
812
|
-
|
|
800
|
+
' end_of_day_local: "18:30"\n'
|
|
813
801
|
"guardrails:\n"
|
|
814
802
|
" rumination_threshold: 4\n"
|
|
815
803
|
" rumination_window_minutes: 120\n"
|
|
816
|
-
|
|
804
|
+
' sycophancy_check: "off"\n'
|
|
817
805
|
)
|
|
818
806
|
parsed = _parse_profile_text(sample_profile)
|
|
819
807
|
expected = {
|
|
@@ -834,7 +822,7 @@ def _self_test() -> int:
|
|
|
834
822
|
ok = False
|
|
835
823
|
|
|
836
824
|
# Profile parsing: an empty / commented profile yields no overrides.
|
|
837
|
-
if _parse_profile_text(
|
|
825
|
+
if _parse_profile_text('# just a comment\nschema_version: "0.1.0"\n') != {}:
|
|
838
826
|
sys.stderr.write("FAIL: bare profile should yield no overrides\n")
|
|
839
827
|
ok = False
|
|
840
828
|
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://schemas.neurodock.org/neurotype-addenda/v1/neurotype-addenda.schema.json",
|
|
4
|
+
"title": "NeuroDock Neurotype Addenda Artifact",
|
|
5
|
+
"description": "Validates the language-neutral, enum-keyed prompt-shaping content artifact (data/neurotype-addenda/v1.json). This artifact is CONTENT, not schema shape (ADR 0011): it carries the per-(tool x neurotype) prose blocks that NeuroDock surfaces append to a model prompt, plus the fusion rule, priority ordering, framing, output-format guidance, and the cross-cutting voice-input / tourette / other / generic-fallback blocks. Additive-only versioning: new tools, new neurotypes, and new optional top-level keys are non-breaking. Unknown keys are permitted for forward-compatibility where appropriate.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": true,
|
|
8
|
+
"required": [
|
|
9
|
+
"artifact_version",
|
|
10
|
+
"fusion",
|
|
11
|
+
"priority",
|
|
12
|
+
"framing",
|
|
13
|
+
"output_format",
|
|
14
|
+
"voice_input",
|
|
15
|
+
"tourette",
|
|
16
|
+
"other",
|
|
17
|
+
"generic",
|
|
18
|
+
"tools"
|
|
19
|
+
],
|
|
20
|
+
"properties": {
|
|
21
|
+
"$schema": {
|
|
22
|
+
"type": "string",
|
|
23
|
+
"description": "Optional pointer back to this schema's $id."
|
|
24
|
+
},
|
|
25
|
+
"artifact_version": {
|
|
26
|
+
"type": "string",
|
|
27
|
+
"description": "Semantic version of the artifact content. Additive changes bump minor; a breaking re-shape forks to a new vN.json + a new schema $id.",
|
|
28
|
+
"pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$",
|
|
29
|
+
"examples": ["1.0.0"]
|
|
30
|
+
},
|
|
31
|
+
"description": {
|
|
32
|
+
"type": "string"
|
|
33
|
+
},
|
|
34
|
+
"tokens": {
|
|
35
|
+
"type": "array",
|
|
36
|
+
"description": "The complete set of interpolation tokens any block may contain. Documentation only; the assembler is the authority.",
|
|
37
|
+
"items": { "type": "string" }
|
|
38
|
+
},
|
|
39
|
+
"fusion": {
|
|
40
|
+
"type": "object",
|
|
41
|
+
"description": "The AuDHD substitution rule. When the input set lists the `result` neurotype directly, OR lists every neurotype in `all_of`, the `result` block is substituted and every neurotype in `remove` is dropped.",
|
|
42
|
+
"additionalProperties": true,
|
|
43
|
+
"required": ["result", "all_of", "remove"],
|
|
44
|
+
"properties": {
|
|
45
|
+
"description": { "type": "string" },
|
|
46
|
+
"result": { "type": "string" },
|
|
47
|
+
"any_of": {
|
|
48
|
+
"type": "array",
|
|
49
|
+
"items": { "type": "string" }
|
|
50
|
+
},
|
|
51
|
+
"all_of": {
|
|
52
|
+
"type": "array",
|
|
53
|
+
"items": { "type": "string" }
|
|
54
|
+
},
|
|
55
|
+
"remove": {
|
|
56
|
+
"type": "array",
|
|
57
|
+
"items": { "type": "string" }
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"priority": {
|
|
62
|
+
"type": "array",
|
|
63
|
+
"description": "Neurotype ordering. Higher-priority addenda are placed LATER in the assembled prompt (recency bias). Neurotypes are sorted by their index in this list.",
|
|
64
|
+
"items": { "type": "string" },
|
|
65
|
+
"minItems": 1
|
|
66
|
+
},
|
|
67
|
+
"framing": {
|
|
68
|
+
"type": "object",
|
|
69
|
+
"description": "The wrapper, header, footer, separators, and conflict footer that surround the per-neurotype blocks.",
|
|
70
|
+
"additionalProperties": true,
|
|
71
|
+
"required": [
|
|
72
|
+
"wrapper_prefix",
|
|
73
|
+
"wrapper_suffix",
|
|
74
|
+
"section_separator",
|
|
75
|
+
"block_line_separator",
|
|
76
|
+
"header",
|
|
77
|
+
"footer",
|
|
78
|
+
"conflict_footer_min_neurotypes",
|
|
79
|
+
"conflict_footer"
|
|
80
|
+
],
|
|
81
|
+
"properties": {
|
|
82
|
+
"wrapper_prefix": { "type": "string" },
|
|
83
|
+
"wrapper_suffix": { "type": "string" },
|
|
84
|
+
"section_separator": { "type": "string" },
|
|
85
|
+
"block_line_separator": { "type": "string" },
|
|
86
|
+
"header": {
|
|
87
|
+
"type": "array",
|
|
88
|
+
"items": { "type": "string" }
|
|
89
|
+
},
|
|
90
|
+
"footer": {
|
|
91
|
+
"type": "array",
|
|
92
|
+
"items": { "type": "string" }
|
|
93
|
+
},
|
|
94
|
+
"conflict_footer_min_neurotypes": {
|
|
95
|
+
"type": "integer",
|
|
96
|
+
"minimum": 1
|
|
97
|
+
},
|
|
98
|
+
"conflict_footer": { "type": "string" }
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
"output_format": {
|
|
102
|
+
"type": "object",
|
|
103
|
+
"description": "Per-output-format guidance. The assembler renders `prefix + format + separator + descriptions[format]`.",
|
|
104
|
+
"additionalProperties": true,
|
|
105
|
+
"required": ["prefix", "separator", "descriptions", "default"],
|
|
106
|
+
"properties": {
|
|
107
|
+
"prefix": { "type": "string" },
|
|
108
|
+
"separator": { "type": "string" },
|
|
109
|
+
"descriptions": {
|
|
110
|
+
"type": "object",
|
|
111
|
+
"description": "Map of output-format enum value to its one-line description.",
|
|
112
|
+
"additionalProperties": { "type": "string" },
|
|
113
|
+
"minProperties": 1
|
|
114
|
+
},
|
|
115
|
+
"default": { "type": "string" }
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
"voice_input": {
|
|
119
|
+
"type": "object",
|
|
120
|
+
"description": "Cross-cutting voice-input block. Emitted when voice input is preferred, before the per-neurotype blocks.",
|
|
121
|
+
"additionalProperties": true,
|
|
122
|
+
"required": ["block"],
|
|
123
|
+
"properties": {
|
|
124
|
+
"block": { "$ref": "#/$defs/block" }
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
"tourette": {
|
|
128
|
+
"type": "object",
|
|
129
|
+
"description": "Tourette special block. Tool-independent; rendered whenever `tourette` is in the effective set.",
|
|
130
|
+
"additionalProperties": true,
|
|
131
|
+
"required": ["block"],
|
|
132
|
+
"properties": {
|
|
133
|
+
"block": { "$ref": "#/$defs/block" }
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
"other": {
|
|
137
|
+
"type": "object",
|
|
138
|
+
"description": "Self-described block. Carries the {notes} interpolation token; rendered when `other` is selected with notes, or appended as a footer when notes are present without `other`.",
|
|
139
|
+
"additionalProperties": true,
|
|
140
|
+
"required": ["block"],
|
|
141
|
+
"properties": {
|
|
142
|
+
"block": { "$ref": "#/$defs/block" }
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
"generic": {
|
|
146
|
+
"type": "object",
|
|
147
|
+
"description": "Generic per-neurotype fallback blocks (no tool context, or no concrete per-tool block for the pair). Keyed by neurotype enum value.",
|
|
148
|
+
"additionalProperties": { "$ref": "#/$defs/block" }
|
|
149
|
+
},
|
|
150
|
+
"tools": {
|
|
151
|
+
"type": "object",
|
|
152
|
+
"description": "Per-tool matrices of concrete per-neurotype blocks. Keyed by tool name, then by neurotype enum value. Additive: a new tool or a new neurotype within a tool is non-breaking.",
|
|
153
|
+
"additionalProperties": {
|
|
154
|
+
"type": "object",
|
|
155
|
+
"additionalProperties": { "$ref": "#/$defs/block" }
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
"$defs": {
|
|
160
|
+
"block": {
|
|
161
|
+
"type": "array",
|
|
162
|
+
"description": "A prose block: an ordered list of lines joined with the framing block_line_separator. Lines may contain {max_chunk_size} and/or {notes} interpolation tokens.",
|
|
163
|
+
"items": { "type": "string" },
|
|
164
|
+
"minItems": 1
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
@@ -62,6 +62,15 @@ preferences:
|
|
|
62
62
|
# system_default — your OS choice
|
|
63
63
|
reading_font_hint: "atkinson_hyperlegible"
|
|
64
64
|
|
|
65
|
+
# How much space do you want between lines of text?
|
|
66
|
+
# This is a hint for apps that show NeuroDock's output as styled text.
|
|
67
|
+
# Options:
|
|
68
|
+
# compact — lines sit closer together (more text fits on screen)
|
|
69
|
+
# default — the app's own everyday spacing
|
|
70
|
+
# relaxed — extra space between lines; pairs with atkinson_hyperlegible
|
|
71
|
+
# Leave it out and the app just uses its own spacing.
|
|
72
|
+
line_height_hint: "relaxed"
|
|
73
|
+
|
|
65
74
|
# Do you want visual animations turned on?
|
|
66
75
|
# Options:
|
|
67
76
|
# reduced — no animations (default; safest for vestibular sensitivity)
|
|
@@ -90,6 +90,17 @@
|
|
|
90
90
|
"enum": ["reduced", "system", "full"],
|
|
91
91
|
"default": "reduced",
|
|
92
92
|
"description": "Animation policy hint. 'reduced' = no animation, no transitions, no auto-scroll (default). 'system' = follow the OS prefers-reduced-motion setting. 'full' = animations allowed."
|
|
93
|
+
},
|
|
94
|
+
"line_height_hint": {
|
|
95
|
+
"type": "string",
|
|
96
|
+
"enum": ["compact", "default", "relaxed"],
|
|
97
|
+
"description": "Optional (added v0.1.x per ADR 0011). Line-height hint for any client that renders NeuroDock output as HTML or rich text, sitting alongside 'reading_font_hint'. A categorical band rather than a raw multiplier so each client maps it to its own CSS line-height. CONFORMANCE FLOOR: clients MUST NOT render body-paragraph line spacing below 1.5 regardless of band (WCAG 1.4.8 Visual Presentation / 1.4.12 Text Spacing). The bands are advisory line-height ratios relative to font size for body paragraphs; headings and other non-body text MAY be tighter. Per-band anchors: 'compact' ≈ 1.5 — the floor itself, a power-user opt-in for more text per viewport that still respects the 1.5 minimum (for readers who find generous spacing scatters their eye-line and prefer denser text, the dual of why 'relaxed' exists); 'default' ≈ 1.5–1.6 — the client's own comfortable default within the conformant range; 'relaxed' ≈ 1.65–1.8 — generous line spacing (~1.65 is the project body line-height) and the evidence-based pairing with 'atkinson_hyperlegible' for dyslexia, which is why dyslexia-aware presets set it. Read-when-present, neutral-when-absent: an untailored client ignores it; absence is NOT an error. The loader applies no value when absent. Self-ID never gates this (ADR 0004/0011).",
|
|
98
|
+
"examples": ["relaxed", "default"]
|
|
99
|
+
},
|
|
100
|
+
"voice_input_preferred": {
|
|
101
|
+
"type": "boolean",
|
|
102
|
+
"description": "Optional (added v0.1.x per ADR 0011). When true, the user predominantly dictates rather than types for sustained work (common for dyspraxic users). Downstream skills that emit code or structured text MUST NOT assume the user can hand-edit fiddly punctuation cheaply: keep examples copy-pasteable as a single block rather than scattered across inline edits. Read by the shaping layer; absence means 'no preference' and is the same as today's behaviour. Consumer pending; schema-only in this release.",
|
|
103
|
+
"examples": [true]
|
|
93
104
|
}
|
|
94
105
|
}
|
|
95
106
|
},
|
|
@@ -121,6 +132,62 @@
|
|
|
121
132
|
"enum": ["auto_close", "error"],
|
|
122
133
|
"default": "auto_close",
|
|
123
134
|
"description": "Behaviour of mark_session_start when a prior session is still open. 'auto_close' (default, charitable) closes the prior session and returns its metadata so the skill can surface it. 'error' refuses to start a new session until the prior one is explicitly closed. See ADR 0001, open question 3."
|
|
135
|
+
},
|
|
136
|
+
"calendar_phase": {
|
|
137
|
+
"type": "string",
|
|
138
|
+
"enum": ["teaching", "marking", "exam", "deadlines", "break"],
|
|
139
|
+
"description": "Optional (added v0.1.x per ADR 0011). Self-declared phase of the user's term/semester so skills can shift defaults across the calendar: tighter break cadence during 'marking' / 'exam', deadline-cluster awareness during 'deadlines' (e.g. week 12), looser during 'break'. Surfaced by the educator-semester and student-university presets. The neurotype is never a branch point (ADR 0004/0011); this is a user input the shaping layer reads, neutral-when-absent. Consumer pending (mcp-chronometric); schema-only in this release.",
|
|
140
|
+
"examples": ["teaching", "marking"]
|
|
141
|
+
},
|
|
142
|
+
"weekday_overrides": {
|
|
143
|
+
"type": "object",
|
|
144
|
+
"description": "Optional (added v0.1.x per ADR 0011). Per-weekday overrides for 'end_of_day_local' and 'hyperfocus_break_minutes', covering late-office-hours, Wednesday-afternoon-class, and library-day-vs-lecture-day patterns without forcing a whole-profile swap. Keys are lowercase English weekday names; only the seven weekdays are accepted (a misspelt key is a silent no-op, so unknown keys are rejected here rather than preserved). Each value is an override object reusing the same patterns/ranges as the top-level chronometric fields; an empty override object is valid. A weekday absent from the map inherits the top-level chronometric values. Consumer pending (mcp-chronometric); schema-only in this release.",
|
|
145
|
+
"additionalProperties": false,
|
|
146
|
+
"properties": {
|
|
147
|
+
"monday": { "$ref": "#/$defs/weekdayOverride" },
|
|
148
|
+
"tuesday": { "$ref": "#/$defs/weekdayOverride" },
|
|
149
|
+
"wednesday": { "$ref": "#/$defs/weekdayOverride" },
|
|
150
|
+
"thursday": { "$ref": "#/$defs/weekdayOverride" },
|
|
151
|
+
"friday": { "$ref": "#/$defs/weekdayOverride" },
|
|
152
|
+
"saturday": { "$ref": "#/$defs/weekdayOverride" },
|
|
153
|
+
"sunday": { "$ref": "#/$defs/weekdayOverride" }
|
|
154
|
+
},
|
|
155
|
+
"examples": [
|
|
156
|
+
{
|
|
157
|
+
"wednesday": { "end_of_day_local": "18:30" },
|
|
158
|
+
"saturday": { "hyperfocus_break_minutes": 120 }
|
|
159
|
+
}
|
|
160
|
+
]
|
|
161
|
+
},
|
|
162
|
+
"protected_windows": {
|
|
163
|
+
"type": "array",
|
|
164
|
+
"description": "Optional (added v0.1.x per ADR 0011). Local-time ranges (lunch, post-EOD evening, scheduled lectures, lab times) where the hyperfocus monitor should HARD-SURFACE rather than nudge — the strict rung of the escalation ladder, applied because the window itself is protected, not because a session has run long. Each entry is a 'HH:MM'-'HH:MM' range with an optional human label; ranges are interpreted in the user's local timezone and a range whose 'end' is earlier than its 'start' is treated as wrapping past midnight by the consumer. An empty list is valid. Consumer pending (mcp-chronometric); schema-only in this release.",
|
|
165
|
+
"items": { "$ref": "#/$defs/protectedWindow" },
|
|
166
|
+
"examples": [
|
|
167
|
+
[
|
|
168
|
+
{ "start": "12:00", "end": "12:30", "label": "lunch" },
|
|
169
|
+
{ "start": "17:00", "end": "23:59", "label": "evening" }
|
|
170
|
+
]
|
|
171
|
+
]
|
|
172
|
+
},
|
|
173
|
+
"deadline_cluster_awareness": {
|
|
174
|
+
"type": "boolean",
|
|
175
|
+
"description": "Optional (added v0.1.x per ADR 0011). When true, planning skills surface deadline proximity and shift the break-cadence math when work clusters (e.g. three assignments due in a single week). Set by the student-university preset. Read by planning skills; absence means today's behaviour (no clustering adjustment). Consumer pending (task-fractionator); schema-only in this release.",
|
|
176
|
+
"examples": [true]
|
|
177
|
+
},
|
|
178
|
+
"time_buffer_multiplier": {
|
|
179
|
+
"type": "number",
|
|
180
|
+
"minimum": 1.0,
|
|
181
|
+
"maximum": 3.0,
|
|
182
|
+
"default": 1.0,
|
|
183
|
+
"description": "Optional (added v0.1.x per ADR 0011). Multiplier that pads presented time estimates because real-world execution time for motor-heavy tasks is systematically underestimated (e.g. 1.3 = +30%, the value the dyspraxia preset sets). Range 1.0..3.0; the neutral default 1.0 reproduces today's unpadded behaviour so an untouched profile is unchanged. Read by planning skills to scale the estimates they present; the underlying estimate is not altered. Consumer pending (task-fractionator); schema-only in this release.",
|
|
184
|
+
"examples": [1.0, 1.3]
|
|
185
|
+
},
|
|
186
|
+
"motor_fatigue_aware": {
|
|
187
|
+
"type": "boolean",
|
|
188
|
+
"default": false,
|
|
189
|
+
"description": "Optional (added v0.1.x per ADR 0011). When true, the hyperfocus monitor weights motor activity (click / keystroke / window-switch volume) into the fatigue signal rather than relying on continuous-session length alone — cognitive sharpness and motor exhaustion can coexist (set by the dyspraxia preset). The neutral default false reproduces today's session-length-only behaviour. Reading motor activity is still gated by 'privacy.os_idle_consent' and the relevant OS-input consents; this flag only declares the preference. Consumer pending (mcp-chronometric); schema-only in this release.",
|
|
190
|
+
"examples": [true]
|
|
124
191
|
}
|
|
125
192
|
}
|
|
126
193
|
},
|
|
@@ -176,6 +243,55 @@
|
|
|
176
243
|
}
|
|
177
244
|
}
|
|
178
245
|
},
|
|
246
|
+
"$defs": {
|
|
247
|
+
"weekdayOverride": {
|
|
248
|
+
"type": "object",
|
|
249
|
+
"description": "Per-weekday override for the chronometric fields that vary by day. Both members are optional and reuse the same validation as their top-level counterparts; an empty object means 'this weekday is named but inherits the top-level values'. additionalProperties is false because an unknown key here is almost always a typo that would silently do nothing.",
|
|
250
|
+
"additionalProperties": false,
|
|
251
|
+
"properties": {
|
|
252
|
+
"end_of_day_local": {
|
|
253
|
+
"type": "string",
|
|
254
|
+
"pattern": "^([01][0-9]|2[0-3]):[0-5][0-9]$",
|
|
255
|
+
"description": "Override of 'chronometric.end_of_day_local' for this weekday, as 'HH:MM' (24h) in the user's local timezone. Same pattern as the top-level field.",
|
|
256
|
+
"examples": ["17:00", "18:30"]
|
|
257
|
+
},
|
|
258
|
+
"hyperfocus_break_minutes": {
|
|
259
|
+
"type": "integer",
|
|
260
|
+
"minimum": 15,
|
|
261
|
+
"maximum": 240,
|
|
262
|
+
"description": "Override of 'chronometric.hyperfocus_break_minutes' for this weekday. Same 15..240 range as the top-level field; re-anchors the 'nudge' rung for this day only.",
|
|
263
|
+
"examples": [60, 120]
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
},
|
|
267
|
+
"protectedWindow": {
|
|
268
|
+
"type": "object",
|
|
269
|
+
"description": "A single local-time range where the hyperfocus monitor hard-surfaces. 'start' and 'end' are required 'HH:MM' (24h) strings; 'label' is an optional human-readable name. additionalProperties is false to catch typos in this small, fixed shape.",
|
|
270
|
+
"additionalProperties": false,
|
|
271
|
+
"required": ["start", "end"],
|
|
272
|
+
"properties": {
|
|
273
|
+
"start": {
|
|
274
|
+
"type": "string",
|
|
275
|
+
"pattern": "^([01][0-9]|2[0-3]):[0-5][0-9]$",
|
|
276
|
+
"description": "Window start, 'HH:MM' (24h), local timezone.",
|
|
277
|
+
"examples": ["12:00", "17:00"]
|
|
278
|
+
},
|
|
279
|
+
"end": {
|
|
280
|
+
"type": "string",
|
|
281
|
+
"pattern": "^([01][0-9]|2[0-3]):[0-5][0-9]$",
|
|
282
|
+
"description": "Window end, 'HH:MM' (24h), local timezone. An 'end' earlier than 'start' is treated by the consumer as wrapping past midnight.",
|
|
283
|
+
"examples": ["12:30", "23:59"]
|
|
284
|
+
},
|
|
285
|
+
"label": {
|
|
286
|
+
"type": "string",
|
|
287
|
+
"minLength": 1,
|
|
288
|
+
"maxLength": 80,
|
|
289
|
+
"description": "Optional human-readable label for the window, surfaced in any notice the monitor emits.",
|
|
290
|
+
"examples": ["lunch", "evening", "lecture"]
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
},
|
|
179
295
|
"examples": [
|
|
180
296
|
{
|
|
181
297
|
"identity": {
|