catalyst-os 3.0.0 → 3.0.2

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.
@@ -149,18 +149,20 @@ function install() {
149
149
  console.log(` ${green}✓${reset} Installed .claude/skills`);
150
150
  }
151
151
 
152
- // Install config files
153
- const configs = [
154
- ['.catalyst/spec-structure.yaml', '.catalyst/spec-structure.yaml'],
155
- ['.catalyst/main/project-config.yaml', '.catalyst/main/project-config.yaml']
156
- ];
157
-
158
- for (const [srcFile, destFile] of configs) {
159
- const srcPath = path.join(src, srcFile);
160
- const destPath = path.join(cwd, destFile);
161
- if (copyFile(srcPath, destPath)) {
162
- console.log(` ${green}✓${reset} Installed ${destFile}`);
152
+ // Framework-owned config: always refresh on (re)install.
153
+ if (copyFile(path.join(src, '.catalyst/spec-structure.yaml'), path.join(cwd, '.catalyst/spec-structure.yaml'))) {
154
+ console.log(` ${green}✓${reset} Installed .catalyst/spec-structure.yaml`);
155
+ }
156
+
157
+ // User-owned config: create ONLY if missing — never clobber a customized
158
+ // project-config.yaml (branch settings, voice.language, agent_id, etc.) on update.
159
+ const pcDest = path.join(cwd, '.catalyst/main/project-config.yaml');
160
+ if (!fs.existsSync(pcDest)) {
161
+ if (copyFile(path.join(src, '.catalyst/main/project-config.yaml'), pcDest)) {
162
+ console.log(` ${green}✓${reset} Installed .catalyst/main/project-config.yaml`);
163
163
  }
164
+ } else {
165
+ console.log(` ${dim}•${reset} Kept existing .catalyst/main/project-config.yaml`);
164
166
  }
165
167
 
166
168
  // Install voice runtime (zero-dependency local meeting daemon for /meet-spec)
@@ -113,13 +113,33 @@
113
113
  notes.innerHTML = lastNotesHtml;
114
114
  }
115
115
 
116
+ let voices = [];
117
+ function loadVoices() { try { voices = speechSynthesis.getVoices() || []; } catch {} }
118
+ if ('speechSynthesis' in window) { loadVoices(); speechSynthesis.onvoiceschanged = loadVoices; }
119
+ function pickVoice() {
120
+ if (!voices.length) loadVoices();
121
+ const base = lang.split('-')[0].toLowerCase();
122
+ return voices.find(v => v.lang === lang)
123
+ || voices.find(v => (v.lang || '').toLowerCase().startsWith(base))
124
+ || voices[0] || null;
125
+ }
116
126
  function speak(text) {
117
127
  return new Promise((resolve) => {
118
- if (!('speechSynthesis' in window)) return resolve();
128
+ if (!('speechSynthesis' in window) || !text) return resolve();
129
+ try { speechSynthesis.cancel(); } catch {}
119
130
  const u = new SpeechSynthesisUtterance(text);
120
- u.lang = lang; u.onend = resolve; u.onerror = resolve;
131
+ u.lang = lang;
132
+ const v = pickVoice();
133
+ if (v) u.voice = v; // explicit voice → avoids silent "no voice for lang"
134
+ let done = false;
135
+ const finish = () => { if (done) return; done = true; clearInterval(ka); resolve(); };
136
+ u.onend = finish; u.onerror = finish;
137
+ // Chrome silently pauses long utterances (~15s); nudge it to keep going.
138
+ const ka = setInterval(() => { try { if (speechSynthesis.speaking) speechSynthesis.resume(); } catch {} }, 4000);
121
139
  setOrb('speaking'); setStatus('Catalyst is speaking…');
122
140
  speechSynthesis.speak(u);
141
+ // If speech never starts (blocked / no voice), don't hang the turn loop.
142
+ setTimeout(() => { if (!done && !speechSynthesis.speaking) finish(); }, 2500);
123
143
  });
124
144
  }
125
145
 
@@ -170,6 +190,8 @@
170
190
 
171
191
  startBtn.onclick = () => {
172
192
  startBtn.disabled = true; endBtn.disabled = false; chatRow.hidden = false;
193
+ // Unlock audio within the user gesture so TTS works later, and warm up voices.
194
+ if ('speechSynthesis' in window) { try { speechSynthesis.cancel(); speechSynthesis.resume(); loadVoices(); } catch {} }
173
195
  recognition = buildRecognition();
174
196
  if (!recognition) { setStatus('Speech needs Chrome. You can still type below.'); setOrb(''); return; }
175
197
  startListening();
package/AGENTS.md CHANGED
@@ -14,7 +14,7 @@ Quick reference for all agents and the skills they use.
14
14
  |----------|--------------|
15
15
  | Research phase | Oracle + Seer + Scout + Surveyor |
16
16
  | Build phase | Smith x N + Shaper x M (multiple instances) |
17
- | Validation phase | Inquisitor + Watcher + Sentinel |
17
+ | Validation phase | Inquisitor + Watcher + Sentinel + Curator (DB specs) |
18
18
 
19
19
  ### Run Sequentially (Has Dependencies)
20
20
 
@@ -94,6 +94,7 @@ All builders follow `test-driven-development`, `systematic-debugging`, `verifica
94
94
  | [Sentinel](.claude/agents/sentinel.md) | E2E tests | — |
95
95
  | [Inquisitor](.claude/agents/inquisitor.md) | Code review | — |
96
96
  | [Watcher](.claude/agents/watcher.md) | Security audit | — |
97
+ | [Curator](.claude/agents/curator.md) | Schema-integrity audit (read-only DB) | `secret-scanning` |
97
98
 
98
99
  ---
99
100
 
package/README.md CHANGED
@@ -9,6 +9,7 @@
9
9
  ```bash
10
10
  npx catalyst-os # Install to your project
11
11
  /catalyze-project # Initialize project foundation
12
+ /meet-spec "topic" # (optional) Talk it through -> meeting notes
12
13
  /catalyze-spec "description" # Shape a feature specification
13
14
  /challenge-spec @spec-name # (optional) Interrogate the spec branch by branch
14
15
  /forge-spec @spec-name # Implement with TDD
@@ -44,6 +45,7 @@ Then run `/catalyze-project` to initialize — this detects your workspace type,
44
45
  │ ├── agent-delegation/
45
46
  │ ├── receiving-code-review/
46
47
  │ ├── workspace-detection/
48
+ │ ├── meet-spec/
47
49
  │ ├── spec-shaping/
48
50
  │ ├── spec-challenge/
49
51
  │ ├── build-orchestration/
@@ -57,6 +59,8 @@ Then run `/catalyze-project` to initialize — this detects your workspace type,
57
59
 
58
60
  .catalyst/
59
61
  ├── main/ # Mission, roadmap, tech-stack, project-config
62
+ ├── voice/ # /meet-spec meeting runtime (zero-dependency)
63
+ ├── meetings/ # Meeting notes artifacts (generated, git-ignored)
60
64
  ├── specs/ # Active specifications
61
65
  ├── completed/ # Archived specs
62
66
  └── library/ # Reusable patterns
@@ -103,6 +107,7 @@ Without this, skills are optional documentation. With it, they're mandatory proc
103
107
 
104
108
  | Skill | Command | Purpose |
105
109
  |-------|---------|---------|
110
+ | `meet-spec` | `/meet-spec` | Voice meeting → `/catalyze-spec`-ready notes (optional) |
106
111
  | `spec-shaping` | `/catalyze-spec` | Shape feature requests into specifications |
107
112
  | `spec-challenge` | `/challenge-spec` | Interrogate a shaped spec branch by branch (optional) |
108
113
  | `build-orchestration` | `/forge-spec` | DAG-based TDD implementation |
@@ -143,7 +148,8 @@ Guardians (Quality)
143
148
  ├── Enforcer → Unit tests
144
149
  ├── Sentinel → E2E tests
145
150
  ├── Inquisitor → Code review
146
- └── Watcher → Security
151
+ ├── Watcher → Security
152
+ └── Curator → Schema integrity (DB, read-only)
147
153
  ```
148
154
 
149
155
  ### Agent Capabilities
@@ -167,6 +173,7 @@ Guardians (Quality)
167
173
  | | Sentinel | Run E2E tests |
168
174
  | | Inquisitor | Code review, linting |
169
175
  | | Watcher | Security audit, dependency scan |
176
+ | | Curator | Schema-integrity audit (read-only DB) |
170
177
 
171
178
  ---
172
179
 
@@ -201,6 +208,7 @@ Guardians (Quality)
201
208
  | Command | When to Use | Output |
202
209
  |---------|-------------|--------|
203
210
  | `/catalyze-project` | Start new project | mission.md, roadmap.md, tech-stack.md |
211
+ | `/meet-spec "topic"` | Shape a spec by talking it through (optional) | meeting.html + meeting.md |
204
212
  | `/catalyze-spec "feature"` | New feature request | spec.md, research.md |
205
213
  | `/challenge-spec @slug` | Stress-test the spec before forging (optional) | spec.md (patched), handoff.md (Challenge Log) |
206
214
  | `/forge-spec @slug` | Implement feature | tasks.md (updated) |
@@ -218,6 +226,34 @@ Guardians (Quality)
218
226
 
219
227
  ---
220
228
 
229
+ ## Voice Meetings (`/meet-spec`)
230
+
231
+ Shape a spec by **talking it through**. `/meet-spec` opens a local browser meeting room,
232
+ holds a spoken conversation, and writes a `/catalyze-spec`-ready notes artifact. The
233
+ agent can read the codebase, docs, and the web live during the meeting.
234
+
235
+ ```
236
+ /meet-spec "realtime notifications" # opens the room → talk → End meeting
237
+ /catalyze-spec @.catalyst/meetings/YYYY-MM-DD-{slug}/meeting.md
238
+ ```
239
+
240
+ Two modes (`voice.mode` in `project-config.yaml`):
241
+
242
+ | | **diy** (default) | **bundled** |
243
+ |---|---|---|
244
+ | Speech | Browser Web Speech API | ElevenLabs Conversational AI |
245
+ | Brain | Local `claude -p` | ElevenLabs agent (Claude LLM) |
246
+ | Cost | **Free** (uses your Claude) | Per-minute, BYOK key |
247
+ | Setup | None — just Chrome | API key + agent |
248
+
249
+ The room shows **live meeting notes** (left) beside the **chat history** (right). On
250
+ **End meeting** it saves both `meeting.html` (rendered, self-contained) and `meeting.md`
251
+ (canonical input for `/catalyze-spec`). Notes are written in English regardless of the
252
+ spoken language (`voice.language`, e.g. `"tr"`). Runtime lives in `.catalyst/voice/`
253
+ (zero npm dependencies). See `.catalyst/voice/README.md` for setup.
254
+
255
+ ---
256
+
221
257
  ## Spec Folder Structure
222
258
 
223
259
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "catalyst-os",
3
- "version": "3.0.0",
3
+ "version": "3.0.2",
4
4
  "scripts": {
5
5
  "postinstall": "node .catalyst/bin/install.js",
6
6
  "validate": "node .catalyst/bin/validate-artifacts.js"