@theronap/cortex-mcp 0.9.97 → 0.9.99

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/lib/skills.mjs CHANGED
@@ -131,6 +131,52 @@ function installInto(skillsRoot, skills, { removeNames = [] } = {}) {
131
131
  * @param {{ quiet?: boolean }} opts quiet → only emit on actual change (for the SessionStart hook)
132
132
  * @returns {{ installed: string[], repaired: string[], unchanged: string[], targets: string[] }}
133
133
  */
134
+ // Skills renamed in C3 (ADR-0033). The old name keeps working for one release via a forwarding
135
+ // stub, then both the map and the stubs are deleted.
136
+ //
137
+ // The stub is a REAL skill that delegates, not a message that the command moved. A stub which only
138
+ // announces the rename breaks every caller that invokes the old name from automation — and this repo
139
+ // has a rule about surfaces that report something other than what happened. The model reads the stub,
140
+ // follows the pointer, and does the actual work, so `/cortex-log` behaves exactly as it always did.
141
+ export const RENAMED_SKILLS = {
142
+ 'cortex-log': 'agnoclast-log',
143
+ 'cortex-context': 'agnoclast-context',
144
+ 'cortex-author-docs': 'agnoclast-author-docs',
145
+ 'cortex-walkthrough': 'agnoclast-walkthrough',
146
+ }
147
+
148
+ /** PURE. The forwarding stub's SKILL.md body. */
149
+ export function forwardingStub(oldName, newName) {
150
+ return `---
151
+ name: ${oldName}
152
+ description: Renamed to /${newName}. This forwarding stub keeps the old command working and will be removed in a later release.
153
+ ---
154
+
155
+ This skill is now **${newName}**.
156
+
157
+ Read \`~/.claude/skills/${newName}/SKILL.md\` and follow it in full. Everything it says applies
158
+ here unchanged — this file exists only so \`/${oldName}\` keeps working for automation and habit
159
+ that still names the old command. Do the work the real skill describes; do not stop at this notice.
160
+ `
161
+ }
162
+
163
+ /** Write a forwarding stub for every renamed skill whose new form is present. Best-effort: a stub
164
+ * that cannot be written is a lost alias, never a failed install. */
165
+ function installForwardingStubs(skillsRoot, log) {
166
+ for (const [oldName, newName] of Object.entries(RENAMED_SKILLS)) {
167
+ try {
168
+ if (!existsSync(join(skillsRoot, newName, 'SKILL.md'))) continue
169
+ const dir = join(skillsRoot, oldName)
170
+ const file = join(dir, 'SKILL.md')
171
+ const body = forwardingStub(oldName, newName)
172
+ if (existsSync(file) && readFileSync(file, 'utf8') === body) continue
173
+ ensureDir(file)
174
+ writeFileSync(file, body)
175
+ log(` ✓ ${oldName} → forwards to ${newName}`)
176
+ } catch { /* an alias is a convenience, never a reason to fail the install */ }
177
+ }
178
+ }
179
+
134
180
  export function installSkills(opts = {}) {
135
181
  const quiet = !!opts.quiet
136
182
  const log = (m) => { if (!quiet) process.stdout.write(m + '\n') }
@@ -151,6 +197,7 @@ export function installSkills(opts = {}) {
151
197
  summary.unchanged.push(...r.unchanged)
152
198
  const changed = [...r.installed, ...r.repaired]
153
199
  if (changed.length) log(` ✓ ${cli.id}: ${changed.join(', ')} → ${skillsRoot}`)
200
+ installForwardingStubs(skillsRoot, log)
154
201
  }
155
202
 
156
203
  if (quiet && (summary.installed.length || summary.repaired.length)) {
@@ -361,6 +408,35 @@ export async function runSkills(argv = []) {
361
408
  if (argv[0] === 'push') return runSkillsPush(argv.slice(1))
362
409
 
363
410
  const quiet = argv.includes('--quiet')
411
+
412
+ // ── C2 (ADR-0033 §5): move the MCP config key to its new name. ─────────────
413
+ // This runs from the SessionStart hook because that is the only channel that reaches every seat
414
+ // without asking anyone to do anything. It is safe here for two reasons that both matter:
415
+ //
416
+ // • It is SELF-GUARDING. runKeyMigration adds the new namespace's allow rules and re-reads them
417
+ // from disk BEFORE flipping the key, and aborts leaving the machine on the old name if they
418
+ // did not land. The C1 release already pre-authorized both namespaces, so on a seat that took
419
+ // C1 there is nothing left to add — but a seat that skipped C1 entirely is handled too, which
420
+ // is the whole reason the guard lives on the machine rather than in the release cadence.
421
+ // • It is idempotent and cheap after the first run: a state file read, then nothing.
422
+ //
423
+ // FAIL-SOFT, like every other hook step. A migration hiccup must never block a session from
424
+ // starting; the next SessionStart tries again.
425
+ try {
426
+ const { runKeyMigration } = await import('./migrate_key.mjs')
427
+ const res = runKeyMigration()
428
+ if (!quiet && res.status === 'migrated') {
429
+ process.stdout.write(` ✓ MCP config key moved ${res.from} → ${res.to}. Restart to load it under the new name.\n`)
430
+ }
431
+ if (res.status === 'aborted') {
432
+ // Loud on stderr even when quiet: an abort means the machine is INTENTIONALLY still on the old
433
+ // key, and a silent abort is indistinguishable from never having tried.
434
+ process.stderr.write(`agnoclast: key migration held off at "${res.step}" (${res.reason}) — still on the old name, will retry.\n`)
435
+ }
436
+ } catch (e) {
437
+ process.stderr.write(`agnoclast: key migration skipped (${e?.message ?? e}) — will retry next session.\n`)
438
+ }
439
+
364
440
  if (!quiet) process.stdout.write('\nAgnoclast skills — installing managed skills…\n')
365
441
  const r = installSkills({ quiet })
366
442
  if (!quiet) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theronap/cortex-mcp",
3
- "version": "0.9.97",
3
+ "version": "0.9.99",
4
4
  "description": "Connect your AI assistant to Cortex — your org's projects, activity, gaps, and directives, scoped to you.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: cortex-author-docs
2
+ name: agnoclast-author-docs
3
3
  description: Push new/changed documentation (specs, plans, design docs) from disk into Agnoclast as authored wiki pages. Run after writing a spec/plan/design doc, when the user asks to sync docs to Agnoclast, or as part of session close-out.
4
4
  ---
5
5
 
@@ -17,7 +17,7 @@ the doc and author a synthesis. Never dump raw markdown into a page.
17
17
 
18
18
  - Right after you write or substantially update a spec/plan/design/runbook doc on disk.
19
19
  - When the user asks to push/sync docs to Agnoclast.
20
- - During session close-out (`/cortex-log` runs this as a sweep step).
20
+ - During session close-out (`/agnoclast-log` runs this as a sweep step).
21
21
 
22
22
  ## Steps
23
23
 
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: cortex-context
2
+ name: agnoclast-context
3
3
  description: Automatically hydrate Agnoclast context at the start of a substantive session. Use when Agnoclast MCP is available and the user has made a real request, so the first answer is grounded in query-centered org context instead of the static baseline alone.
4
4
  ---
5
5
 
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: cortex-log
2
+ name: agnoclast-log
3
3
  description: Close out a work session into Agnoclast — summarize what happened, confirm it reached the org, and surface anything teammates should know. Run at or near the end of any working session.
4
4
  ---
5
5
 
@@ -62,7 +62,7 @@ No arguments. Read the conversation context.
62
62
  already authored a node mid-session and nothing changed since, `author` will report "no change" —
63
63
  that's fine.
64
64
  6. **Sweep pending documentation** — run `npx -y @theronap/cortex-mcp docs-scan --json`; if any
65
- docs are pending, follow the `cortex-author-docs` skill (author each into its page, then
65
+ docs are pending, follow the `agnoclast-author-docs` skill (author each into its page, then
66
66
  `docs-scan --mark`). Specs/plans written to disk this session must not die on disk — a spec IS
67
67
  a page. If no roots are registered or nothing is pending, skip silently.
68
68
  7. **Reconcile the sweep (don't trust it).** Step 5 relies on your in-the-moment judgment of "what
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: cortex-walkthrough
2
+ name: agnoclast-walkthrough
3
3
  description: Run the guided Agnoclast walkthrough for someone new. Use when the person asks for the walkthrough, a tutorial, or getting started — "give me the walkthrough", "walk me through this", "how do I use this", "show me around", "what can this do", "remind me how this works" — or when a brand-new user needs orienting for the first time.
4
4
  ---
5
5