@noesis-brain/mcp-server 2.0.1 → 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +91 -3
- package/dist/agent/runner.d.ts +29 -0
- package/dist/agent/runner.d.ts.map +1 -0
- package/dist/agent/runner.js +306 -0
- package/dist/agent/runner.js.map +1 -0
- package/dist/api/NoesisClient.d.ts +119 -0
- package/dist/api/NoesisClient.d.ts.map +1 -1
- package/dist/api/NoesisClient.js +130 -6
- package/dist/api/NoesisClient.js.map +1 -1
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +409 -35
- package/dist/cli/setup.js.map +1 -1
- package/dist/index.js +31 -1
- package/dist/index.js.map +1 -1
- package/dist/resolve/noteRef.d.ts +101 -0
- package/dist/resolve/noteRef.d.ts.map +1 -0
- package/dist/resolve/noteRef.js +186 -0
- package/dist/resolve/noteRef.js.map +1 -0
- package/dist/resolve/rootsFormat.d.ts +12 -0
- package/dist/resolve/rootsFormat.d.ts.map +1 -0
- package/dist/resolve/rootsFormat.js +63 -0
- package/dist/resolve/rootsFormat.js.map +1 -0
- package/dist/tools/backlog.d.ts +4 -0
- package/dist/tools/backlog.d.ts.map +1 -0
- package/dist/tools/backlog.js +161 -0
- package/dist/tools/backlog.js.map +1 -0
- package/dist/tools/index.d.ts +92 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +529 -207
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/navis.d.ts.map +1 -1
- package/dist/tools/navis.js +4 -0
- package/dist/tools/navis.js.map +1 -1
- package/package.json +8 -3
- package/scripts/agent-e2e.mjs +69 -0
- package/scripts/noesis-capture-session-end.mjs +94 -0
- package/scripts/noesis-capture-watcher.mjs +953 -0
- package/skill-templates/noesis-capture.md +166 -0
- package/skill-templates/noesis-create-study-note.md +149 -0
- package/skill-templates/noesis-find-related.md +89 -0
- package/skill-templates/noesis-investigate.md +90 -0
- package/skill-templates/noesis-manage-news.md +80 -0
- package/skill-templates/noesis-skim-read.md +72 -0
- package/templates/claude-md-block.md +5 -1
- package/templates/claude-md-restructure-on-sync.md +10 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Continuously capture one or more Claude Code sessions into Noesis notes — a deterministic Node watcher tails each transcript, renders readable Markdown, and pushes it to the Noesis cloud on every change (zero tokens, no model loop). Use when the user says 'capture', 'start capture', 'capture session', 'monitor session', 'mirror session to noesis', or '/noesis-capture <id-or-name>'. Sub-commands - stop [<id|name>], status, sync.
|
|
3
|
+
argument-hint: "<session-id | name> | stop [<id|name>] | status | sync"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# /noesis-capture — Live Session Capture into Noesis (multi-session)
|
|
7
|
+
|
|
8
|
+
You are executing the `/noesis-capture` slash command. It runs in a **second** Claude session (the **controller** session) and mirrors one or more **other** sessions into Noesis notes that update in place — both locally and in the cloud — as those sessions progress.
|
|
9
|
+
|
|
10
|
+
**Architecture (fully deterministic — no model in the periodic path).** A bundled Node watcher does *everything that must run continuously*: it tails the source transcript, renders JSONL→Markdown to the local `.md`, **and pushes that note to the Noesis cloud directly** (`POST /api/mcp/notes/upsert`) on every change. One watcher process runs per captured session. Both the local render and the cloud push cost **zero tokens** and need **no Claude session in the loop**. This controller session only does what genuinely needs a Claude session: resolve targets, start/stop watchers, and report status.
|
|
11
|
+
|
|
12
|
+
The watcher path is `{{NOESIS_CAPTURE_WATCHER_PATH}}` — invoke it with `node "<that path>"`.
|
|
13
|
+
|
|
14
|
+
> There is **no** model-driven sync loop and **no** wake-up/reschedule here. That earlier design was unreliable — a single missed/no-op model turn, an `Esc`, or a non-resumed restart killed cloud sync permanently while the local file kept updating. The watcher now owns the cloud push, so cloud freshness is as reliable as the local render. **Never** reintroduce a model-driven periodic sync.
|
|
15
|
+
|
|
16
|
+
The watcher writes each capture note to `<outPath>` — an absolute path the watcher itself chooses: a `captures/` folder under a registered Noesis root (it prefers the standard `~/Noesis` root, already home-expanded), so the cloud push always finds a matching root. You get the exact `outPath` from the watcher's `--resolve-only` output (Step 1) — **do not compute it yourself from `list_roots`** (stored root paths can be in `~`/`%USERPROFILE%` form and must not be passed to the watcher literally). Alongside the note the watcher writes a per-session status sidecar `<dir>/.<session-id>.cloud.json` (last push time / action / error — read by the `status` sub-command for cloud freshness). All controller state lives in a single shared file **`~/.claude/noesis-capture-state.json`** (v3 schema below), keyed by session id so multiple captures coexist. If the optional `SessionEnd` hook is installed (via `noesis-mcp setup --with-capture`), it marks every capture stopped and kills lingering watchers when this controller session ends; otherwise stop them yourself with `/noesis-capture stop`.
|
|
17
|
+
|
|
18
|
+
**Cloud auth (handled by the watcher).** The watcher reads `NOESIS_API_TOKEN` / `NOESIS_API_URL` from its environment, else from `~/.claude.json → mcpServers.noesis.env`. The token is never logged or placed on a command line. If no token is found, the watcher runs **local-only** (renders but does not push); in that case the cloud copy is maintained only by the skill's `mcp__noesis__sync_notes` calls at Start / Stop / `sync`.
|
|
19
|
+
|
|
20
|
+
## Arguments
|
|
21
|
+
|
|
22
|
+
The raw argument string is: `$ARGUMENTS`
|
|
23
|
+
|
|
24
|
+
Forms:
|
|
25
|
+
- `<session-id>` (a UUID) or `<name>` (a substring of the session's ai-title / summary), optionally followed by `--max-life <dur>` (or a trailing bare `<dur>`) — **start** capturing that session (adds to any already running). `--max-life` sets an optional **hard lifetime cap**: the capture auto-stops (with a final sync) after `<dur>` of wall-clock time.
|
|
26
|
+
- `stop [<id|name>]` — stop **all** captures, or just the one matching `<id|name>`. Final cloud sync each stopped note.
|
|
27
|
+
- `status` — report every capture currently tracked (incl. cloud freshness).
|
|
28
|
+
- `sync` — one-shot: force an immediate cloud sync of every capture via `mcp__noesis__sync_notes`. **Not a loop** — the watcher already keeps the cloud current; use this only to force/confirm a push right now.
|
|
29
|
+
- empty — treat as `status`.
|
|
30
|
+
|
|
31
|
+
**Duration `<dur>`** (for `--max-life`): one or both of `<N>h` and `<N>m` — e.g. `2h`, `90m`, `1h30m`. Convert to milliseconds as `(hours*60 + minutes) * 60000`; treat as **no cap** if it parses to 0 or is absent. Default: no cap (monitor until `/noesis-capture stop`). The cap is **per-capture**.
|
|
32
|
+
|
|
33
|
+
## State schema (`~/.claude/noesis-capture-state.json`, version 3)
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"version": 3,
|
|
38
|
+
"controllerSessionId": "<this-capture-session-id (SELF)>",
|
|
39
|
+
"sessions": {
|
|
40
|
+
"<session-id>": {
|
|
41
|
+
"title": "<display-title> — Capture",
|
|
42
|
+
"outPath": "<absolute path returned by --resolve-only>",
|
|
43
|
+
"transcriptPath": "<path-to-source-.jsonl>",
|
|
44
|
+
"bgTaskId": "<background-task-id>",
|
|
45
|
+
"watching": true,
|
|
46
|
+
"startedAt": 0,
|
|
47
|
+
"maxLifetimeMs": null,
|
|
48
|
+
"lastChangeTime": 0
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
- **Global:** `version`, `controllerSessionId` (this controller's own id / `SELF` — lets the optional `SessionEnd` hook auto-dispose captures when the controller closes).
|
|
55
|
+
- **Per-session** (under `sessions[<id>]`): `title`, `outPath`, `transcriptPath`, `bgTaskId`, `watching`, `startedAt` (unix-ms the capture began — the lifetime-cap clock), `maxLifetimeMs` (hard cap in ms, or `null`), `lastChangeTime` (unix-ms). The session id is the map key — do not duplicate it inside the object.
|
|
56
|
+
- All timestamps are **unix milliseconds**. Cloud freshness is **not** stored here — it lives in each watcher's `.<id>.cloud.json` sidecar next to the note.
|
|
57
|
+
|
|
58
|
+
### Read + migrate (do this at the top of every sub-command that touches state)
|
|
59
|
+
|
|
60
|
+
Read `~/.claude/noesis-capture-state.json` with the Read tool and normalize **in memory** to v3:
|
|
61
|
+
- Missing / unparseable → start a fresh v3 object: `{version:3, controllerSessionId:null, sessions:{}}`.
|
|
62
|
+
- Has a `sessions` object (v2 or v3) → keep `sessions`; **drop** any obsolete model-loop / cost-guard globals if present (`baseDelaySeconds`, `maxDelaySeconds`, `idleLevel`, `lastSyncTime`, `nextSyncTime`, `model`, `effort`, `costGuardOk`). For each session, default `startedAt` to its `lastChangeTime` (or `<now-ms>` if unset) and `maxLifetimeMs` to `null` when absent. Set `version:3`.
|
|
63
|
+
- Has a top-level `sessionId` (the **old singleton** format) → fold it into `sessions[sessionId]`, drop legacy globals, set `version:3`, `controllerSessionId:null` (recorded on the next Start/Sync).
|
|
64
|
+
|
|
65
|
+
Only **Start**, **Sync**, and **Stop** write the file back (Write tool, whole object). **Status** never writes.
|
|
66
|
+
|
|
67
|
+
## Instructions
|
|
68
|
+
|
|
69
|
+
### Step 0 — Discover THIS session id (for the self-monitor guard)
|
|
70
|
+
|
|
71
|
+
Ask the watcher for the current session's id so it can refuse to capture itself:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
node "{{NOESIS_CAPTURE_WATCHER_PATH}}" --print-self
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
It prints `SELF=<id>` (possibly empty if it can't resolve — then omit `--self`). Keep `SELF` for the `--self` flag.
|
|
78
|
+
|
|
79
|
+
The watcher picks the output location itself (a `captures/` folder under a registered Noesis root, home-expanded), so you do **not** resolve roots or build paths here — you get the exact `outPath` back from `--resolve-only` in the Start step.
|
|
80
|
+
|
|
81
|
+
### Step 1 — Route on the sub-command
|
|
82
|
+
|
|
83
|
+
- `stop` (with or without an argument) -> go to **Stop** (pass the remainder of the argument).
|
|
84
|
+
- `status` or empty -> go to **Status**.
|
|
85
|
+
- `sync` -> go to **Sync (one-shot)**.
|
|
86
|
+
- anything else -> treat the whole argument as a session id/name and go to **Start**.
|
|
87
|
+
|
|
88
|
+
### Start — `/noesis-capture <id-or-name>`
|
|
89
|
+
|
|
90
|
+
1. **Parse the lifetime cap, then resolve.** First strip an optional lifetime cap from `$ARGUMENTS`: a `--max-life <dur>` / `--max-life=<dur>` flag, or a trailing standalone `<dur>` token (see **Duration**). Convert it to `maxLifetimeMs` (or `null`). The remaining text is `<target>`. Then resolve `<target>` with the watcher's read-only mode and respect the self-guard:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
node "{{NOESIS_CAPTURE_WATCHER_PATH}}" --resolve-only --self "<SELF>" --session "<target>"
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
- Non-zero exit / `ERROR: could not resolve` -> tell the user no session matched and stop. Suggest the session id (UUID) or a more specific name fragment.
|
|
97
|
+
- Exit code 3 (`REFUSED: self-monitor guard`) -> they pointed capture at the current session; refuse and stop.
|
|
98
|
+
- Success -> parse the JSON (`sessionId`, `transcriptPath`, `title`, `outPath`, `candidates`). Use the returned **`outPath` as-is** (an absolute path under a registered Noesis root) for state, sync, and sidecar — do not recompute it. If `candidates` has more than one entry, briefly say which match was chosen (most recently modified) and list the alternatives.
|
|
99
|
+
|
|
100
|
+
2. **Read + migrate state.** Compute `activeCount` = number of sessions with `watching === true` **before** this start (informational only — there is no shared loop to start).
|
|
101
|
+
|
|
102
|
+
3. **Dedup / restart guard** on `resolved.sessionId`:
|
|
103
|
+
- Already present AND `watching === true` -> tell the user it's already being captured (its `title` + `outPath`) and offer a restart. On restart: `TaskStop` the old `bgTaskId` (tolerate failure), then continue and overwrite just that one entry. Never spawn a second watcher for the same id.
|
|
104
|
+
- Present AND `watching === false` (previously stopped/paused) -> treat as restart: reuse the entry with a fresh watcher.
|
|
105
|
+
|
|
106
|
+
4. **Decide `startedAt`, then start the watcher in the background** (the long-running tail + render + cloud push).
|
|
107
|
+
- **`startedAt`:** if this is a brand-new entry, or `maxLifetimeMs` was supplied on this call, set `startedAt = <now-ms>` (fresh lifetime window). On a restart with no new duration, keep the existing `startedAt` and `maxLifetimeMs`.
|
|
108
|
+
- Launch the watcher. Append `--max-life-ms <remaining>` **only** when `maxLifetimeMs` is set, where `remaining = max(1000, maxLifetimeMs - (<now-ms> - startedAt))` — the watcher self-exits (after a final cloud push) when its own lifetime elapses:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
node "{{NOESIS_CAPTURE_WATCHER_PATH}}" --session "<sessionId>" --self "<SELF>" --out "<outPath>" [--max-life-ms <remaining>]
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Run with `run_in_background: true`. Record the returned background **task id** as `bgTaskId`. The watcher self-loads the Noesis token and begins pushing to the cloud on its first render — there is **nothing to schedule**.
|
|
115
|
+
|
|
116
|
+
5. **Upsert the session entry** (never overwrite siblings):
|
|
117
|
+
`sessions[sessionId] = { title, outPath, transcriptPath, bgTaskId, watching: true, startedAt, maxLifetimeMs, lastChangeTime: <now-ms> }`.
|
|
118
|
+
Ensure global `version:3` and `controllerSessionId = <SELF>` are present. Write state once.
|
|
119
|
+
|
|
120
|
+
6. **Immediate confirmation sync.** Give the watcher ~2 s to write the first render, then call `mcp__noesis__sync_notes` with `files: ["<outPath>"]` for an instant cloud copy; report `create`/`push`/`skip`. (This is a one-shot confirmation only — thereafter the watcher keeps the cloud current automatically, and it also covers the case where the watcher is running local-only.)
|
|
121
|
+
|
|
122
|
+
7. **Tell the user:** what is being captured (`title` + `sessionId`), the note path, and that **both** the local note and the **cloud** copy now update in real time via the watcher (deterministic, no token cost, no model loop) until `/noesis-capture stop`. If the watcher's sidecar reports `cloudEnabled:false` (no token), say so and note that the cloud copy will only refresh on `/noesis-capture sync` / `stop`.
|
|
123
|
+
|
|
124
|
+
### Sync (one-shot) — `/noesis-capture sync`
|
|
125
|
+
|
|
126
|
+
This is **not** a loop and never reschedules anything. Use it to force/confirm a cloud push right now.
|
|
127
|
+
|
|
128
|
+
1. **Read + migrate state.** If `sessions` is empty or no session has `watching === true` -> say nothing is being captured and stop.
|
|
129
|
+
2. For each `sessions[id]` with `watching === true`: call `mcp__noesis__sync_notes` with `files: ["<outPath>"]`. `skip` (unchanged — the watcher already pushed it) is normal and expected. On `conflict`: report it and tell the user to inspect the note (these notes are auto-generated, so a conflict means the cloud copy was hand-edited); do not auto-resolve.
|
|
130
|
+
3. Keep output to one short line, e.g. "Captures: 2 synced · 1 already current".
|
|
131
|
+
|
|
132
|
+
### Status — `/noesis-capture status`
|
|
133
|
+
|
|
134
|
+
Read + migrate state and report:
|
|
135
|
+
- Global: the controller session id (`controllerSessionId`) and the active count.
|
|
136
|
+
- Per session in `sessions`: `title`, short id (`id[:8]`), `watching` (true/false), **local** last-updated (mtime of `outPath`), **cloud** freshness — read the sidecar `<dirname(outPath)>/.<id>.cloud.json` and report `lastAction` + age of `lastPushTime`, or `lastError` if set, or `local-only` when `cloudEnabled:false` (missing sidecar → "cloud: starting…"), the lifetime cap (from `maxLifetimeMs`/`startedAt` → e.g. "2h cap · 1h12m left", or "no cap"), and `outPath`.
|
|
137
|
+
|
|
138
|
+
If there is no state file or `sessions` is empty, say nothing is being captured.
|
|
139
|
+
|
|
140
|
+
### Stop — `/noesis-capture stop [<id-or-name>]`
|
|
141
|
+
|
|
142
|
+
Read + migrate state first.
|
|
143
|
+
|
|
144
|
+
- **No argument** -> stop **all** `watching:true` sessions. For each: `TaskStop` its `bgTaskId` (if stale/unknown, warn that the node watcher may still be running and that closing it or ending the `noesis-capture-watcher.mjs` process will stop it), then a **final** `mcp__noesis__sync_notes` with its `outPath`, then set `watching:false`. List every stopped session (title + outPath).
|
|
145
|
+
- **With an argument** -> match it against `sessions`: exact id key first; else case-insensitive substring on `title` (if ambiguous, list the matches and ask which). Stop **only that one** (TaskStop + final sync + `watching:false`); **leave siblings running**. Unknown arg -> say it isn't being captured and list the current sessions.
|
|
146
|
+
|
|
147
|
+
Write state once at the end. Confirm to the user with the final note path(s).
|
|
148
|
+
|
|
149
|
+
## Constraints
|
|
150
|
+
|
|
151
|
+
- **Never** sync this skill file or the watcher to Noesis — only the generated capture `.md` notes (the `outPath` files).
|
|
152
|
+
- The watcher owns the **periodic** cloud push; the controller **never** runs a sync loop or reschedule. The only cloud pushes the controller issues are the one-shot `mcp__noesis__sync_notes` at Start (confirmation), `sync`, and Stop (final).
|
|
153
|
+
- Push to Noesis **only** via the watcher or `mcp__noesis__sync_notes` (never `cp` into a folder).
|
|
154
|
+
- Never edit a watcher's output `.md` by hand — it is fully regenerated on every change.
|
|
155
|
+
- Multiple sessions coexist under one state file; each has its own independent watcher. Start adds; it never overwrites a sibling.
|
|
156
|
+
- Capture costs **no tokens while monitoring** (the watcher is a plain Node process), so there is no cost guard, no idle auto-pause, and no model/effort prompt — capture any session on any model.
|
|
157
|
+
|
|
158
|
+
## Examples
|
|
159
|
+
|
|
160
|
+
- `/noesis-capture 1b1bcf99-f520-4bf5-9575-247d7574e8f5` — capture that session by id (adds to any already running)
|
|
161
|
+
- `/noesis-capture Implement auth redesign` — capture by name (ai-title substring)
|
|
162
|
+
- `/noesis-capture 1b1bcf99-… --max-life 2h` — capture with a hard 2-hour lifetime cap; `--max-life 90m` and a trailing `1h30m` also work
|
|
163
|
+
- `/noesis-capture status` — list every capture, with local + cloud freshness
|
|
164
|
+
- `/noesis-capture sync` — force an immediate cloud push of every capture (normally unnecessary)
|
|
165
|
+
- `/noesis-capture stop 1b1bcf99-…` — stop just that one capture, leave the rest running
|
|
166
|
+
- `/noesis-capture stop` — stop all captures and do a final cloud sync of each
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Research a URL or topic from the web and create a polished Noesis study note with structural diagrams, then refine its metadata and sync to the cloud. Use when the user says 'create study note', 'study note from <url>', 'make a note about', 'note from this video/article/URL', or 'summarize this link into a note'."
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Research a source from the web and turn it into a well-structured Noesis study note: fetch and corroborate the material, author accurate prose with proper ASCII diagrams, then fill metadata, link it into your knowledge base, and sync to the cloud. Works from a URL (video, article, docs) or a plain topic.
|
|
6
|
+
|
|
7
|
+
This skill is self-contained — it embeds the note-structure and diagram rules below, so it does not depend on any local reference files or external scripts.
|
|
8
|
+
|
|
9
|
+
## Arguments
|
|
10
|
+
|
|
11
|
+
$ARGUMENTS
|
|
12
|
+
|
|
13
|
+
Accepted forms:
|
|
14
|
+
|
|
15
|
+
- `<url>` — research this page / video / article and build a note from it. Extra URLs after the first are treated as additional sources.
|
|
16
|
+
- `<topic>` or `"quoted title"` — research the topic from scratch (no URL).
|
|
17
|
+
- `--path <file.md>` — explicit output path. Otherwise a path is derived under a registered Noesis root.
|
|
18
|
+
- `--type reference|procedure|investigation|architecture|strategy` — force the content type. Otherwise it is classified.
|
|
19
|
+
- `--catalog <name>` — force a catalog assignment.
|
|
20
|
+
- `--no-sync` — author locally but do not push to the Noesis cloud.
|
|
21
|
+
- `--no-refine` — skip the `/noesis-refine-note` hand-off (just author, enrich, and sync).
|
|
22
|
+
|
|
23
|
+
## Workflow
|
|
24
|
+
|
|
25
|
+
### Step 1 — Parse
|
|
26
|
+
|
|
27
|
+
Detect URL vs topic; extract the flags above and remove them from the subject string. If the subject is empty, ask the user what URL or topic to create a note about, then stop until answered.
|
|
28
|
+
|
|
29
|
+
### Step 2 — Research (never write from memory)
|
|
30
|
+
|
|
31
|
+
1. **Fetch the primary URL** with `WebFetch`, asking for title, author/date, and a full content summary.
|
|
32
|
+
- **YouTube / JS-page caveat:** a `youtube.com/watch` page (or any JS-heavy page) fetched via `WebFetch` usually returns only nav/footer chrome — no transcript or description. When the result is thin, do not give up: use the title as a search seed and rely on the next two sub-steps.
|
|
33
|
+
2. **Web-search** with 2–4 `WebSearch` queries (exact title, product/feature name, "official docs", "announcement"/"release", a how-it-works phrasing) to find the authoritative primary sources and corroborate facts.
|
|
34
|
+
3. **Fetch the top 2–3 primary sources** (official docs, the vendor's own announcement, a reputable deep-dive) with `WebFetch`. Prefer primary over aggregators. Skip any source that returns 403/blocked — do not retry endlessly.
|
|
35
|
+
4. **Build a fact sheet**: the 1-line "what it is", the problem it solves, key mechanics/constraints/requirements, dates/versions, and the **Sources** list (every URL you actually used). Resolve contradictions across sources before writing; if a genuine disagreement cannot be resolved, state it in the note rather than picking silently.
|
|
36
|
+
|
|
37
|
+
### Step 3 — Plan
|
|
38
|
+
|
|
39
|
+
- **Classify** the content type (default `reference` for a feature/concept/talk explainer; `procedure` for a how-to; `investigation` for a bug study; `architecture` for system design; `strategy` for a decision).
|
|
40
|
+
- **Derive identity**: a descriptive title; a 1–2 sentence description (anchor a date/version if time-bound); 6–12 keywords; 3–5 aliases (alternative names / query phrasings).
|
|
41
|
+
- **Derive the path** (unless `--path`): `lowercase-hyphenated-title.md`, punctuation stripped, placed in a registered Noesis root. Call `mcp__noesis__list_roots` and pick a writable root (prefer one ending in `/.noesis` or the `~/Noesis` cloud root). Use today's date (absolute `YYYY-MM-DD`) for `date`/`updated`.
|
|
42
|
+
- **Plan sections** from the content-type table (see rules below); always lead with an Overview. **Plan diagrams** per the trigger checklist and the density tier.
|
|
43
|
+
|
|
44
|
+
### Step 4 — Author the note (one pass)
|
|
45
|
+
|
|
46
|
+
Compose the **entire** note — frontmatter + H1 + TOC + all prose + all diagrams — and `Write` it in a single pass.
|
|
47
|
+
|
|
48
|
+
- **Never** retro-fit diagrams by bulk-replacing fenced blocks in an already-written file: ` ```text ` / ` ```markdown ` fences mis-pair against plain ` ``` ` fences and corrupt the document. Author the whole file at once, or insert via anchored string replacement on a unique heading.
|
|
49
|
+
- Author framed / nested / multi-column / sequence diagrams with the **coordinate-grid generator** (below) so columns are guaranteed to align. Trees (`├── └──`) and tiny 2–3 box flows may be authored inline.
|
|
50
|
+
|
|
51
|
+
### Step 5 — Verify diagrams
|
|
52
|
+
|
|
53
|
+
Re-read every fenced diagram and confirm: ASCII-only inside the fence, right walls aligned, no Unicode arrows (`→ ← ↑ ↓`), no `│` glitches, no trailing spaces. Fix drift by **regenerating** that diagram with the grid script — not by hand-patching columns. If a diagram will not converge after a few tries, surface it rather than claiming success.
|
|
54
|
+
|
|
55
|
+
### Step 6 — Refine metadata + sync (delegate)
|
|
56
|
+
|
|
57
|
+
Unless `--no-refine`, invoke the **`/noesis-refine-note`** skill on the new file by passing just its path (this runs it in single-note mode):
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
/noesis-refine-note <NOTE_PATH>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
That fills missing metadata, refreshes the AI quality/importance scores, and pushes the note to the Noesis cloud. Do not duplicate that logic here.
|
|
64
|
+
|
|
65
|
+
If `--no-refine`: push the note yourself with `mcp__noesis__sync_notes(files: ["<NOTE_PATH>"])`, unless `--no-sync`.
|
|
66
|
+
|
|
67
|
+
### Step 7 — Link it into the knowledge base (this skill)
|
|
68
|
+
|
|
69
|
+
`/noesis-refine-note` does not assign catalogs or create relations, so do those here:
|
|
70
|
+
|
|
71
|
+
1. Get the note id and current cloud metadata: `mcp__noesis__get_note(path: "<NOTE_PATH>")`.
|
|
72
|
+
2. **Persist aliases/keywords** (frontmatter-only additions do not stick in the cloud fields): `mcp__noesis__enhance_note_metadata(note_id, apply_suggestions: { aliases: [...], keywords: [...] })` with your authored values.
|
|
73
|
+
3. **Catalogs**: `mcp__noesis__list_catalogs`, then assign up to 2 confident best-fit catalogs via `mcp__noesis__set_note_catalogs` (honor `--catalog`). Skip if nothing fits.
|
|
74
|
+
4. **Relations**: `mcp__noesis__find_similar_notes(note_id)` (optionally also `mcp__noesis__search_semantic`); create 2–5 genuinely meaningful bidirectional links via `mcp__noesis__update_relations`. Reject weak / generic-vocabulary matches — the cap is a ceiling, not a target.
|
|
75
|
+
|
|
76
|
+
### Step 8 — Report
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
=== STUDY NOTE CREATED ===
|
|
80
|
+
Path: <NOTE_PATH>
|
|
81
|
+
Type: <content type>
|
|
82
|
+
Sections: <N primary> | TOC links verified
|
|
83
|
+
Diagrams: <N> (<types>) — alignment verified
|
|
84
|
+
Sources: <count> (primary: <url>)
|
|
85
|
+
Catalogs: <list> Relations: <count>
|
|
86
|
+
Synced: <yes | skipped (--no-sync)>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Remind the user to **check the diagram rendering** in their markdown viewer (box-drawing alignment) before relying on the note.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Note structure rules (inlined)
|
|
94
|
+
|
|
95
|
+
- **Frontmatter** (YAML between `---`): `title`, `description`, `keywords` (list), `aliases` (list), `date`, `updated` (`YYYY-MM-DD`), `status` (`active`/`draft`/`completed`). Only add fields with real values.
|
|
96
|
+
- **Headings**: exactly one H1 = the title. Major sections are H2. Use **plain `##`** for primary sections (Noesis builds its sidebar outline from them). Reserve collapsible `<details><summary>…</summary>` for noise (raw logs, long tables, references, appendix); put an `id="<slug>"` on a `<summary>` only if you link to it from the TOC.
|
|
97
|
+
- **Table of Contents**: if there are 4+ primary sections, add a standalone `## Table of Contents` immediately below the H1, listing each primary section as `- [Title](#slug)`. Compute slugs GitHub-style: lowercase, strip punctuation (keep `-`/`_`/space), spaces → `-`, collapse repeats. Verify every link resolves.
|
|
98
|
+
- **Content-type sections**: reference → Overview, (Problem It Solves), What It Is, How It Works, core mechanics, Constraints, Availability; procedure → Overview, Prerequisites, Steps, Verification; investigation → Overview, Symptoms, Root Cause, Fix, Verification; architecture → Overview, Components, Data Flow, Key Decisions; strategy → Overview, Context, Recommendation, Action Plan, Risks. Only include sections you have content for.
|
|
99
|
+
- **No emojis** in headings or `<summary>` tags.
|
|
100
|
+
- **Do not** write "Related Notes" / "Related Codebase" sections into the file — those are database metadata (Step 7).
|
|
101
|
+
- End with a `<details><summary>Sources</summary>…</summary>` appendix listing the URLs used.
|
|
102
|
+
|
|
103
|
+
## Diagram rules (inlined)
|
|
104
|
+
|
|
105
|
+
- **ASCII is the default** — author diagrams in an untagged fenced code block. Reserve Mermaid (```` ```mermaid ````) for small (≤10 nodes), balanced decision/hierarchy graphs.
|
|
106
|
+
- **Density** scales with the note: 1–2 diagrams for a short note (≤4 sections), 3–5 for a moderate note (5–7), **6–10 for a complex reference/architecture note** (8+ sections).
|
|
107
|
+
- **Quality bar**: ≥5 elements per diagram (a 3-box A→B→C is a sentence, not a diagram); when a note has 4+ diagrams they must span **≥3 distinct types** from {workflow, component, sequence, state, tree, data-flow, comparison}; every branching diagram labels its secondary/error/HALT path; sequence diagrams show **response** messages, not just requests; component/data-flow diagrams show at least one **labeled direction** arrow.
|
|
108
|
+
- **ASCII-only inside fences**: use box-drawing `─ │ ┌ ┐ └ ┘ ┬ ┴ ├ ┤ ┼` for frames and plain-ASCII markers `> < v ^` for arrows. **Never** use Unicode arrows `→ ← ↑ ↓` inside a fence — editors using Consolas/Courier fall back to a CJK font for them and render at 2 cells wide, breaking column alignment. (Unicode arrows are fine in prose, headings, and tables.)
|
|
109
|
+
- **Type → shape**: workflow/procedure → boxes + branch points; architecture/services → nested boxes with data-flow arrows; request/response → sequence (vertical lifelines, horizontal labeled arrows); lifecycle/status → state boxes + labeled transitions; hierarchy → `├── └──` tree; pipeline → boxes with typed arrows.
|
|
110
|
+
|
|
111
|
+
## Diagram grid generator (alignment technique)
|
|
112
|
+
|
|
113
|
+
Hand-drawn box/framed/sequence ASCII almost always drifts (misaligned right walls, trailing spaces, skewed nested boxes). Generate those diagrams with a small Python coordinate grid that places every wall at a fixed column. Write this to a throwaway script, build each diagram, have the script assemble the whole note (interpolating the rendered diagrams into the prose), then run it to write the file:
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
class Grid:
|
|
117
|
+
def __init__(self, w, h): self.g = [[" "]*w for _ in range(h)]
|
|
118
|
+
def put(self, r, c, s):
|
|
119
|
+
for i, ch in enumerate(s): self.g[r][c+i] = ch
|
|
120
|
+
def box(self, r, c, w, h, lines):
|
|
121
|
+
self.put(r, c, "┌"+"─"*(w-2)+"┐"); self.put(r+h-1, c, "└"+"─"*(w-2)+"┘")
|
|
122
|
+
for i in range(1, h-1): self.put(r+i, c, "│"); self.put(r+i, c+w-1, "│")
|
|
123
|
+
for i, ln in enumerate(lines): self.put(r+1+i, c+2, ln)
|
|
124
|
+
def render(self):
|
|
125
|
+
rows = ["".join(row).rstrip() for row in self.g]
|
|
126
|
+
while rows and rows[-1] == "": rows.pop()
|
|
127
|
+
return "\n".join(rows)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Guidance:
|
|
131
|
+
|
|
132
|
+
- Place boxes at fixed `(row, col)`; connect them with arrows in the gutters between boxes — keep each box's text shorter than its inner width so it never overwrites the right wall.
|
|
133
|
+
- Write the file with `encoding="utf-8", newline="\n"`. Do **not** `print()` box-drawing to a Windows console (cp1252 will crash) — write to the file and read it back to verify.
|
|
134
|
+
- Delete the throwaway script when done if it sits inside a Noesis-watched folder.
|
|
135
|
+
|
|
136
|
+
## Constraints
|
|
137
|
+
|
|
138
|
+
- **Accuracy over volume.** Ground every claim in a fetched source; cite what you used; resolve contradictions before writing.
|
|
139
|
+
- **One-pass authoring.** Never bulk-replace fenced blocks in an existing file — it desyncs on `text`/`markdown` fences.
|
|
140
|
+
- **Self-contained.** Do not reference machine-specific files (e.g. `~/.claude/...`) or external scripts; the rules and the grid technique above are everything you need.
|
|
141
|
+
- **Stay inside a registered Noesis root** so `mcp__noesis__sync_notes` works. Confirm with `mcp__noesis__list_roots`.
|
|
142
|
+
- **Delegate metadata/scores/sync** to `/noesis-refine-note`; this skill owns research, authoring, diagrams, catalogs, and relations.
|
|
143
|
+
|
|
144
|
+
## Examples
|
|
145
|
+
|
|
146
|
+
- `/noesis-create-study-note https://www.youtube.com/watch?v=XXXX` — research a video and build a note.
|
|
147
|
+
- `/noesis-create-study-note "Event sourcing vs CRUD" --type architecture` — research a topic from scratch.
|
|
148
|
+
- `/noesis-create-study-note https://example.com/post --catalog Work` — force a catalog.
|
|
149
|
+
- `/noesis-create-study-note https://example.com/post --no-refine --no-sync` — author locally only.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Discover related Noesis notes for either a NOTE (its formal relations, similar notes, and relation graph) or a CODEBASE (notes matching its tech stack, domain, and concepts). Read-only. Use when the user says 'find related notes', 'related docs', 'discover relations', 'what relates to this', 'what notes relate to this project', or 'find notes for this codebase'.
|
|
3
|
+
argument-hint: "[N results] <note path | note ID | codebase directory | search query>"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# /noesis-find-related — Discover Related Content in Noesis
|
|
7
|
+
|
|
8
|
+
You are executing the `/noesis-find-related` slash command. It discovers related content in the Noesis knowledge base from one of two starting points:
|
|
9
|
+
|
|
10
|
+
- **Note mode** — given a note (ID, `.md` path, filename, or search query), find its formal relations, semantically similar notes, keyword/semantic matches, reverse codebase links, and relation-graph neighbours.
|
|
11
|
+
- **Codebase mode** — given a codebase directory, profile its tech stack / domain / concepts and search Noesis for notes related to it.
|
|
12
|
+
|
|
13
|
+
This is a **read-only** skill — it never writes, edits, syncs, or modifies anything.
|
|
14
|
+
|
|
15
|
+
## Step 0 — Route on the argument
|
|
16
|
+
|
|
17
|
+
The raw arguments string is: `$ARGUMENTS`
|
|
18
|
+
|
|
19
|
+
Parse an optional leading integer **N** (1–50, how many results to display; default 10). Then classify the remaining `<target>`:
|
|
20
|
+
|
|
21
|
+
- **Numeric only** → a note **ID** → Note mode.
|
|
22
|
+
- **Path ending in `.md`** (has `/` or `\`) → a note **path** → Note mode.
|
|
23
|
+
- **`.md` filename** (no path separator) → Note mode (find it via `mcp__noesis__list_notes` matching the filename, or `mcp__noesis__search_notes` on the name — resolve by the Noesis DB, not the filesystem).
|
|
24
|
+
- **Path without `.md`** (a directory, or `.`) → a **codebase directory** → Codebase mode.
|
|
25
|
+
- **Anything else** → a **search query** → Note mode (resolve to a note first).
|
|
26
|
+
- **Empty** → Codebase mode with the current working directory.
|
|
27
|
+
|
|
28
|
+
Clamp N to [1, 50], then go to the matching part below.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Part A — Codebase mode (codebase → related notes)
|
|
33
|
+
|
|
34
|
+
### A1. Analyze the codebase
|
|
35
|
+
Read key identity files (skip any that are absent), in parallel:
|
|
36
|
+
- `Glob` for `{README*,CLAUDE.md,package.json,Cargo.toml,go.mod,pyproject.toml,requirements.txt,*.csproj,*.sln,pom.xml,build.gradle*,docker-compose.yml,Dockerfile}` and `Read` the matches (first ~100 lines of large files).
|
|
37
|
+
- `git -C <dir> log --oneline -20` for recent focus, and `ls` for top-level layout.
|
|
38
|
+
- If `src/` `lib/` `app/` `cmd/` exist, `ls` them; read the main entry point (≤ 2–3 source files total).
|
|
39
|
+
|
|
40
|
+
Synthesize a profile: **PROJECT_NAME**, **PURPOSE** (one sentence), **TECH_STACK**, **DOMAIN**, **KEY_CONCEPTS** (5–10 terms), **RECENT_FOCUS**. Display it. If essentially nothing is found, say so and `AskUserQuestion` for a description or a different path (use a description directly as queries).
|
|
41
|
+
|
|
42
|
+
### A2. Generate 6 diverse queries
|
|
43
|
+
Two each across **tech stack**, **domain**, and **concepts** — each query targeting a genuinely different facet (avoid 6 variations of one search).
|
|
44
|
+
|
|
45
|
+
### A3. Search in parallel (single batch)
|
|
46
|
+
For each of the 6 queries call **both** `mcp__noesis__search_notes` (`limit: N`) and `mcp__noesis__search_semantic` (`limit: min(N,20)`). In the same batch, call `mcp__noesis__search_by_related_code` with the **last folder segment** of the codebase path (never include slashes/backslashes — the lookup is SQL `ILIKE` where `\` escapes; use one distinctive folder name). These reverse-lookup hits are high-confidence explicit links.
|
|
47
|
+
|
|
48
|
+
Error handling: if all searches fail, say Noesis is unavailable and stop. If only `search_semantic` fails (common without a Gemini key), continue keyword-only and note it.
|
|
49
|
+
|
|
50
|
+
### A4. Deduplicate, rank, present
|
|
51
|
+
Group by note ID; per note track hit_count, best_score, categories hit (tech/domain/concept/codebase-link), and search types (keyword/semantic). Rank by a composite that rewards notes found by **many diverse** queries and by the reverse codebase lookup — not just a single high BM25 score. Show the top N as a ranked table (score, title, path, "found via"), then a one-line "why relevant" per result tying it to the profile (from title/path/snippet — do **not** `get_note` each result). Close with brief stats (unique notes, queries with 0 results, semantic available/unavailable).
|
|
52
|
+
|
|
53
|
+
If nothing is found, show the profile and suggest creating notes as you work, or retrying with a different path.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Part B — Note mode (note → related content)
|
|
58
|
+
|
|
59
|
+
### B1. Resolve the target note
|
|
60
|
+
- **ID** → `mcp__noesis__get_note(id)`. **Path** → `mcp__noesis__get_note(path)`. **Query** → `mcp__noesis__search_notes(query, limit:5)`, pick via `AskUserQuestion` if several, then `get_note`.
|
|
61
|
+
- If it fails, show the error and stop.
|
|
62
|
+
|
|
63
|
+
From the response, store: `NOTE_ID`, `NOTE_PATH`, `NOTE_TITLE`, `NOTE_DESCRIPTION`, `NOTE_KEYWORDS`, `NOTE_ALIASES`, `NOTE_RELATIONS` (typed links w/ target_id + context), `NOTE_RELATED_CODEBASES` (resolved objects w/ `path`, `label`), `NOTE_IMPORTANCE`, `NOTE_QUALITY`. Display `Discovering relations for: <title> (ID: <id>)`.
|
|
64
|
+
|
|
65
|
+
### B2. Discover (single parallel batch)
|
|
66
|
+
- **Relation targets:** for each unique `target_id` in `NOTE_RELATIONS`, `get_note` (≤ 10 parallel) to resolve titles.
|
|
67
|
+
- **Similar:** `mcp__noesis__find_similar_notes(note_id, limit:15)`. Record status `ok` / `no_embedding` / `no_api_key`.
|
|
68
|
+
- **Search:** `search_semantic(query: title + first 50 chars of description, limit:10)` and `search_notes(query: keywords or title, limit:10)`. Note if semantic is unavailable.
|
|
69
|
+
- **Reverse codebase:** for each `NOTE_RELATED_CODEBASES` path (≤ 5), `search_by_related_code` with a **single distinctive folder segment** (never slashes/backslashes; dedupe fragments).
|
|
70
|
+
- **Relation graph:** `mcp__noesis__get_relation_graph(note_id, depth:2)` for 2-hop neighbours.
|
|
71
|
+
|
|
72
|
+
### B3. Consolidate and present
|
|
73
|
+
Merge all discovered notes (exclude the target itself by ID). Per note track discovery_sources (e.g. "formal relation", "similar 82%", "keyword", "semantic", "reverse codebase", "graph depth 2"), best_score, and whether it's a formal relation (+ type). Present:
|
|
74
|
+
- **Formal Relations** — type · target title (ID) · context.
|
|
75
|
+
- **Related Codebase** — the linked paths.
|
|
76
|
+
- **Discovered Similar Notes** — notes not already formally related, sorted by best_score (score · title (ID) · discovery · path).
|
|
77
|
+
- **Discovery stats** — counts per method; embedding/semantic availability.
|
|
78
|
+
- **Next actions** — e.g. `/noesis-refine-note <path>` to enrich metadata and create formal relations; `update_relations` to formally link promising matches; `generate_embeddings` if `no_embedding`.
|
|
79
|
+
|
|
80
|
+
If nothing is found, note the note may be new/unique, embeddings may be missing (`generate_embeddings`), or metadata may be thin (`/noesis-refine-note`).
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## Implementation notes
|
|
85
|
+
- **Read-only.** Never write, edit, sync, or modify — this skill only reads and searches.
|
|
86
|
+
- **Parallelism matters.** Batch the codebase file reads (A1) and the discovery calls (A3 / B2) into single parallel batches.
|
|
87
|
+
- **Don't `get_note` every result.** Search/similarity tools return enough metadata (title, path, snippet, score) for the tables; only `get_note` to resolve formal-relation target titles.
|
|
88
|
+
- **Composite ranking over raw scores.** A note surfaced by many diverse queries (or an explicit codebase link) is more relevant than one with a slightly higher single-query score.
|
|
89
|
+
- **Graceful degradation.** Every discovery method is optional — proceed with whatever succeeds (down to keyword-only when Gemini is unavailable).
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Cross-domain context enrichment — given a prompt that mentions codebases AND Noesis notes, resolve the notes and enrich context by querying Noesis (relations, similar notes, codebase links, semantic/keyword search), then produce a consolidated context brief so you can proceed with full awareness. Read-only. Use when the user says 'investigate', 'gather context', 'enrich context', or a prompt references both codebases and notes.
|
|
3
|
+
argument-hint: "<investigation prompt mentioning codebases, notes, aliases, and intent>"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# /noesis-investigate — Cross-Domain Context Enrichment
|
|
7
|
+
|
|
8
|
+
You are executing the `/noesis-investigate` slash command. It takes a natural-language prompt that mentions codebases, Noesis notes, aliases, and/or intent, then enriches it by querying Noesis MCP tools to discover related knowledge. The result is a compiled **context brief** followed by the original prompt re-stated with full awareness, so you can proceed with the actual work.
|
|
9
|
+
|
|
10
|
+
This is a **read-only** skill — it enriches context and never writes, edits, syncs, or modifies anything.
|
|
11
|
+
|
|
12
|
+
## Step 1 — Parse the prompt
|
|
13
|
+
|
|
14
|
+
The raw arguments string is: `$ARGUMENTS`. If empty, check the conversation for a recent investigation request; else `AskUserQuestion` for one. Store it as `INVESTIGATION_PROMPT`.
|
|
15
|
+
|
|
16
|
+
Extract four categories using natural-language understanding (not rigid regex):
|
|
17
|
+
- **Codebases** — directory paths (contain `/` or `\`, do NOT end in `.md`). Store `{full_path, project_name}` where `project_name` is the last folder segment.
|
|
18
|
+
- **Notes** — paths ending in `.md`, bare `.md` filenames, or references like "the note about X". Store `{input, type}` with type `path` | `filename` | `query`.
|
|
19
|
+
- **Aliases** — informal names ("we call it X", "aka X", quoted labels). Store `{term, alias, source}`.
|
|
20
|
+
- **Intent** — a one-sentence summary of the goal (review / understand / compare / audit …).
|
|
21
|
+
|
|
22
|
+
If no codebases AND no notes are found, `AskUserQuestion` for paths, then re-parse. Display a short scope summary (intent, codebases, notes, aliases).
|
|
23
|
+
|
|
24
|
+
## Step 2 — Resolve notes to Noesis IDs (parallel)
|
|
25
|
+
|
|
26
|
+
- **`path`** → `mcp__noesis__get_note(path)`.
|
|
27
|
+
- **`filename`** → find it via `mcp__noesis__list_notes` matching the filename (or `mcp__noesis__search_notes` on the name), then `get_note` by its id/path — resolve by the Noesis DB, not the filesystem.
|
|
28
|
+
- **`query`** → `mcp__noesis__search_notes(query, limit:3)`; use the top hit, falling back to `search_semantic`.
|
|
29
|
+
|
|
30
|
+
For each resolved note store `note_id`, `note_path`, `note_title`, `note_description`, `note_keywords`, `note_aliases`, `note_relations` (typed links w/ target_id), `note_related_codebases` (resolved objects — read `entry.path`). Skip (don't abort on) individual resolution failures. If ALL notes fail and no codebases were given, stop with an error.
|
|
31
|
+
|
|
32
|
+
Merge aliases from the prompt + each note's `note_aliases`. Scan `note_related_codebases` for codebase paths not already listed and add them as `DISCOVERED_CODEBASES`.
|
|
33
|
+
|
|
34
|
+
## Step 3 — Enrichment (single maximum-parallel batch)
|
|
35
|
+
|
|
36
|
+
Dedupe first: collapse repeated note IDs and repeated codebase fragments so each `get_relation_graph` / `find_similar_notes` / `search_by_related_code` runs once. Then issue ALL of the following in one parallel batch:
|
|
37
|
+
|
|
38
|
+
- **Per resolved note:** `mcp__noesis__get_relation_graph(note_id, depth:2)` and `mcp__noesis__find_similar_notes(note_id, limit:10)`.
|
|
39
|
+
- **Per codebase** (input + discovered): `mcp__noesis__search_by_related_code(path: <project_name>, limit:15)`. **Path rule:** pass only a single distinctive folder segment — never slashes/backslashes (the lookup is SQL `ILIKE` where `\` escapes). Dedupe fragments.
|
|
40
|
+
- **Intent:** `mcp__noesis__search_semantic(query: INTENT, limit:15)` and `mcp__noesis__search_notes(query: INTENT key terms + alias values, limit:15)`.
|
|
41
|
+
- **Aliases** not already covered by the intent search: `mcp__noesis__search_notes(query: <alias>, limit:5)`.
|
|
42
|
+
|
|
43
|
+
Error handling: any individual call may fail (no embedding, no Gemini key, empty codebase link) — continue with the rest; record `SEMANTIC_AVAILABLE = false` if semantic search fails. Never abort for a single failure.
|
|
44
|
+
|
|
45
|
+
## Step 4 — Deduplicate and score
|
|
46
|
+
|
|
47
|
+
Collect results with a `discovery_source` tag (`relation-graph:<note>`, `similar:<note>`, `codebase-link:<project>`, `intent-semantic`, `intent-keyword`, `alias:<value>`), a `score`, and graph `depth` where applicable. Group by `note_id`, **excluding the input notes themselves**. Per unique note aggregate `discovery_sources`, `source_count`, `best_score`, and `is_formally_related`.
|
|
48
|
+
|
|
49
|
+
Composite relevance (0–100): reward notes found by **many distinct sources**, formally related, linked via a codebase, or reached from 2+ different input notes — not just a single high score. Sort descending and partition into Tier 1 (≥ 60, full detail), Tier 2 (40–59, compact), Tier 3 (< 40, count only).
|
|
50
|
+
|
|
51
|
+
## Step 5 — Compile the context brief
|
|
52
|
+
|
|
53
|
+
Present:
|
|
54
|
+
- **Header** — intent; counts (input notes / codebases / aliases; discovered notes / codebases); semantic available?
|
|
55
|
+
- **Input entities** — input notes (id, title, path, relation/related-code/alias counts) and input codebases (project, path, notes-linked).
|
|
56
|
+
- **Aliases** — term → alias (source: prompt / Noesis).
|
|
57
|
+
- **Discovered notes** — Tier 1 (score, title (id), found-via, path), Tier 2 (compact), Tier 3 (count only). Do **not** `get_note` discovered notes — search/similarity metadata is enough.
|
|
58
|
+
- **Additionally discovered codebases** — any found in note metadata but not in the prompt (path · referenced-by). Skip if none.
|
|
59
|
+
- **Connection map** — how input entities connect through discovered notes / shared codebases / aliases. Skip if none.
|
|
60
|
+
- **Enrichment stats** — MCP calls issued, raw vs deduped counts, tier sizes, failed calls.
|
|
61
|
+
|
|
62
|
+
## Step 6 — Proceed with enriched context
|
|
63
|
+
|
|
64
|
+
After the brief, hand off to the actual work:
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
Original request:
|
|
68
|
+
> [INVESTIGATION_PROMPT — full original text]
|
|
69
|
+
|
|
70
|
+
Key facts to carry forward:
|
|
71
|
+
- [Alias]: "[term]" is also known as "[alias]"
|
|
72
|
+
- [Connection]: [Note X] and [Note Y] connect through [relation / shared codebase]
|
|
73
|
+
- [Discovery]: [Title] (score N) may hold important context about [topic]
|
|
74
|
+
- [Coverage gap]: [N] codebases have no Noesis documentation yet
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Then continue in the same conversation with full awareness — read the input notes and codebases as needed to fulfill the request. This skill does **not** spawn sub-agents or write anything.
|
|
78
|
+
|
|
79
|
+
**Follow-up actions:**
|
|
80
|
+
- `/noesis-find-related <note ID or path>` — deep-dive one note's relation web, or a codebase's related notes.
|
|
81
|
+
- `/noesis-refine-note <note path>` — enrich a discovered note's metadata and formal relations.
|
|
82
|
+
- `/noesis-create-study-note <topic or URL>` — turn the investigation into a new structured note.
|
|
83
|
+
|
|
84
|
+
## Implementation notes
|
|
85
|
+
- **Read-only.** Enriches context only — never writes, edits, or syncs.
|
|
86
|
+
- **Maximum parallelism.** Step 3 must issue all MCP calls in a single parallel batch.
|
|
87
|
+
- **Only input notes get `get_note`.** Discovered notes use search/similarity metadata (title, path, score).
|
|
88
|
+
- **Best-effort extraction.** Extract what you can from natural language and proceed; don't over-confirm every entity.
|
|
89
|
+
- **`search_by_related_code` path rule:** always a single folder name, no slashes/backslashes.
|
|
90
|
+
- **Graceful degradation.** Every enrichment call is optional; works keyword-only when semantic/embeddings are unavailable, and still presents the input entities even if all enrichment fails.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Manage Daily News preferences on Noesis — add/remove sources, analyze preferences, discover new feeds. Use when the user says 'manage news', 'news preferences', 'add news source', 'find RSS feeds', or wants to manage their Daily News tool configuration via natural language.
|
|
3
|
+
argument-hint: "<natural language instruction about news preferences>"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# /noesis-manage-news — Manage Daily News Preferences
|
|
7
|
+
|
|
8
|
+
You are executing the `/noesis-manage-news` slash command. This is a **natural-language, prompt-driven** skill for managing the user's Daily News preferences in Noesis via MCP tools.
|
|
9
|
+
|
|
10
|
+
## Arguments
|
|
11
|
+
|
|
12
|
+
$ARGUMENTS
|
|
13
|
+
|
|
14
|
+
## Available MCP Tools
|
|
15
|
+
|
|
16
|
+
You have access to these Noesis MCP tools:
|
|
17
|
+
- `mcp__noesis__get_news_preferences` — Read current preferences (topics, sources, seeds, weights, language, daily limit)
|
|
18
|
+
- `mcp__noesis__update_news_preferences` — Update preferences (language, daily limit, topic/source weights)
|
|
19
|
+
- `mcp__noesis__add_news_source` — Add an RSS feed source (name, URL, topic, language)
|
|
20
|
+
|
|
21
|
+
## Instructions
|
|
22
|
+
|
|
23
|
+
Parse the user's natural language intent and dispatch to the appropriate MCP tools. Common intents:
|
|
24
|
+
|
|
25
|
+
### "Add this news URL" / "I like this article"
|
|
26
|
+
1. Extract the domain from the URL
|
|
27
|
+
2. Use web search to find the RSS feed for that domain (search for `"{domain}" RSS feed URL`)
|
|
28
|
+
3. Call `add_news_source` with the discovered feed URL, domain name, and an appropriate topic category
|
|
29
|
+
4. Report what was added
|
|
30
|
+
|
|
31
|
+
### "Summarize my preferences" / "What am I reading?"
|
|
32
|
+
1. Call `get_news_preferences`
|
|
33
|
+
2. Present a human-readable summary: top topics by weight, active sources, language, daily limit
|
|
34
|
+
3. Highlight any topics or sources with very low weights (< 0.2) that might need attention
|
|
35
|
+
|
|
36
|
+
### "Remove topic X" / "I don't like X anymore"
|
|
37
|
+
1. Call `get_news_preferences` to get current state
|
|
38
|
+
2. Identify matching topics and sources
|
|
39
|
+
3. Call `update_news_preferences` to set the topic weight to 0 and/or remove related source weights
|
|
40
|
+
4. Report what was changed
|
|
41
|
+
|
|
42
|
+
### "Find RSS feeds about [topic]" / "Add [topic] news"
|
|
43
|
+
1. Use web search to find reputable RSS feeds for the topic (search for `"{topic}" RSS feed`, `"{topic}" news RSS`)
|
|
44
|
+
2. Validate discovered URLs are actual RSS feeds
|
|
45
|
+
3. Call `add_news_source` for each valid feed
|
|
46
|
+
4. Report what was added
|
|
47
|
+
|
|
48
|
+
### "Change language to [X]"
|
|
49
|
+
1. Call `update_news_preferences` with the new `preferred_language`
|
|
50
|
+
2. Suggest that existing sources may not match the new language
|
|
51
|
+
|
|
52
|
+
### "Set daily limit to N"
|
|
53
|
+
1. Call `update_news_preferences` with the new `daily_article_limit`
|
|
54
|
+
|
|
55
|
+
## Language Awareness
|
|
56
|
+
|
|
57
|
+
When adding sources or searching for feeds, respect the user's preferred language (check via `get_news_preferences` if not specified). Search for feeds in that language. Common language codes: `en`, `zh-TW`, `zh-CN`, `ja`, `ko`, `fr`, `de`, `es`.
|
|
58
|
+
|
|
59
|
+
## Response Style
|
|
60
|
+
|
|
61
|
+
- Be concise — report what you did, not how you did it
|
|
62
|
+
- After making changes, show a brief summary of the updated state
|
|
63
|
+
- If the user's intent is ambiguous, ask for clarification before making changes
|
|
64
|
+
|
|
65
|
+
## Examples
|
|
66
|
+
|
|
67
|
+
User: "I like this news https://arstechnica.com/tech-policy/2026/04/eu-ai-act/"
|
|
68
|
+
Action: Search for Ars Technica RSS feed, add it as a source with topic "Technology"
|
|
69
|
+
|
|
70
|
+
User: "What are my news preferences?"
|
|
71
|
+
Action: Call get_news_preferences, present summary
|
|
72
|
+
|
|
73
|
+
User: "Find me some Japanese tech news feeds"
|
|
74
|
+
Action: Web search for Japanese tech RSS feeds, add discovered feeds with language "ja"
|
|
75
|
+
|
|
76
|
+
User: "Remove the World Politics topic"
|
|
77
|
+
Action: Get preferences, set World Politics weight to 0, report change
|
|
78
|
+
|
|
79
|
+
User: "I want 30 articles per day"
|
|
80
|
+
Action: Update daily_article_limit to 30
|