@noesis-brain/mcp-server 2.1.5 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +10 -1
  2. package/dist/agent/identity.d.ts +33 -0
  3. package/dist/agent/identity.d.ts.map +1 -0
  4. package/dist/agent/identity.js +61 -0
  5. package/dist/agent/identity.js.map +1 -0
  6. package/dist/agent/runner.d.ts +103 -6
  7. package/dist/agent/runner.d.ts.map +1 -1
  8. package/dist/agent/runner.js +375 -64
  9. package/dist/agent/runner.js.map +1 -1
  10. package/dist/agent/tokenState.d.ts +81 -0
  11. package/dist/agent/tokenState.d.ts.map +1 -0
  12. package/dist/agent/tokenState.js +173 -0
  13. package/dist/agent/tokenState.js.map +1 -0
  14. package/dist/agent/webGuard.d.ts +48 -0
  15. package/dist/agent/webGuard.d.ts.map +1 -0
  16. package/dist/agent/webGuard.js +173 -0
  17. package/dist/agent/webGuard.js.map +1 -0
  18. package/dist/api/NoesisClient.d.ts +19 -1
  19. package/dist/api/NoesisClient.d.ts.map +1 -1
  20. package/dist/api/NoesisClient.js +55 -3
  21. package/dist/api/NoesisClient.js.map +1 -1
  22. package/dist/cli/setup.js +1 -1
  23. package/dist/cli/setup.js.map +1 -1
  24. package/dist/machineLabel.d.ts +16 -0
  25. package/dist/machineLabel.d.ts.map +1 -0
  26. package/dist/machineLabel.js +43 -0
  27. package/dist/machineLabel.js.map +1 -0
  28. package/dist/tools/backlog.d.ts +21 -0
  29. package/dist/tools/backlog.d.ts.map +1 -1
  30. package/dist/tools/backlog.js +123 -1
  31. package/dist/tools/backlog.js.map +1 -1
  32. package/dist/tools/claudeSessions.d.ts +83 -0
  33. package/dist/tools/claudeSessions.d.ts.map +1 -0
  34. package/dist/tools/claudeSessions.js +330 -0
  35. package/dist/tools/claudeSessions.js.map +1 -0
  36. package/dist/tools/index.d.ts +1 -6
  37. package/dist/tools/index.d.ts.map +1 -1
  38. package/dist/tools/index.js +166 -37
  39. package/dist/tools/index.js.map +1 -1
  40. package/dist/tools/sessionIndex.d.ts +66 -0
  41. package/dist/tools/sessionIndex.d.ts.map +1 -0
  42. package/dist/tools/sessionIndex.js +241 -0
  43. package/dist/tools/sessionIndex.js.map +1 -0
  44. package/package.json +1 -1
  45. package/scripts/noesis-capture-watcher.mjs +722 -107
  46. package/scripts/start-agent.sh +51 -12
  47. package/skill-templates/noesis-refine-relations.md +163 -0
  48. package/skill-templates/noesis-ticket.md +79 -0
  49. package/templates/claude-md-block.md +7 -0
@@ -23,6 +23,7 @@
23
23
  # ./scripts/start-agent.sh --local # this checkout's dist/ (after npm run build)
24
24
  # ./scripts/start-agent.sh --status # report running daemons, exit 1 if any is unfixed
25
25
  # ./scripts/start-agent.sh --stop # stop everything, start nothing
26
+ # ./scripts/start-agent.sh --grade <package-dir> # grade one build; touches no process
26
27
 
27
28
  set -euo pipefail
28
29
 
@@ -39,8 +40,11 @@ MIN_FIXED_VERSION="2.1.5"
39
40
  MODE="${1:-}"
40
41
  case "$MODE" in
41
42
  ''|--local|--stop|--status) ;;
43
+ --grade)
44
+ [ -n "${2:-}" ] || { echo "usage: $(basename "$0") --grade <package-dir>" >&2; exit 2; }
45
+ ;;
42
46
  *)
43
- echo "usage: $(basename "$0") [--local | --status | --stop]" >&2
47
+ echo "usage: $(basename "$0") [--local | --status | --stop | --grade <package-dir>]" >&2
44
48
  # Deliberately NOT a silent fallback: a typo'd flag used to start the PUBLISHED build
45
49
  # instead of the local one — the same "wrong build served the job" signature.
46
50
  exit 2
@@ -121,7 +125,27 @@ pkg_dir_for_pid() {
121
125
  # comments before looking for code.
122
126
  strip_js_comments() { perl -0pe 's{/\*.*?\*/}{}gs; s{^[[:space:]]*//.*$}{}gm' "$1" 2>/dev/null || true; }
123
127
 
124
- # Prints "<version> <FIXED|UNFIXED(reason)|PARENT>" for a pid.
128
+ # True when compiled runner.js (comments already stripped, on stdin) restricts the SDK's
129
+ # built-in tools. Two accepted shapes:
130
+ # - `tools: []` — 2.1.5 to 2.1.x: no built-ins at all;
131
+ # - `tools: webTools` built by `clampWebTools(` over the literal two-name list
132
+ # — 2.2.0+: none, or at most WebFetch + WebSearch, which have
133
+ # no filesystem access (web Navis; see runner.ts).
134
+ # Recognising ONLY the first shape graded every 2.2.0 daemon UNFIXED and the assert at the
135
+ # bottom then killed it: a guaranteed outage on the first restart after publishing.
136
+ # The version floor stays 2.1.5 — the filesystem boundary is what this guards.
137
+ has_tool_boundary() {
138
+ local code; code="$(cat)"
139
+ # Here-strings, not `printf | grep -q`: under pipefail an early-exiting grep can SIGPIPE
140
+ # the writer once the file outgrows the pipe buffer, turning a match into a failure.
141
+ grep -q 'canUseTool:' <<<"$code" || return 1
142
+ grep -q 'tools: \[\]' <<<"$code" && return 0
143
+ grep -q 'tools: webTools,' <<<"$code" \
144
+ && grep -q 'const webTools = clampWebTools(' <<<"$code" \
145
+ && grep -q "ALLOWED_WEB_TOOLS = \['WebFetch', 'WebSearch'\]" <<<"$code"
146
+ }
147
+
148
+ # Prints "<version> <FIXED|UNFIXED(reason)>" for a package directory.
125
149
  #
126
150
  # TWO INDEPENDENT SIGNALS, both required:
127
151
  # - the package version is >= MIN_FIXED_VERSION (the same signal the backend's
@@ -129,25 +153,40 @@ strip_js_comments() { perl -0pe 's{/\*.*?\*/}{}gs; s{^[[:space:]]*//.*$}{}gm' "$
129
153
  # - the compiled output actually contains both halves of the fix, comments removed.
130
154
  # An earlier version compared neither: MIN_FIXED_VERSION was assigned and never used, and
131
155
  # the content check matched documentation. "v2.1.5 FIXED" was two non-facts side by side.
132
- describe_pid() {
133
- local pid="$1" dir ver stripped ver_ok=0 code_ok=0
134
- dir="$(pkg_dir_for_pid "$pid")"
135
- # No package dir = the `npm exec` wrapper, not a daemon. Its child is graded separately;
136
- # counting the parent as "unknown, therefore exposed" cried wolf on every healthy npx
137
- # launch, which is the mode that becomes correct once the fix is published.
138
- if [ -z "$dir" ] || [ ! -f "$dir/package.json" ]; then echo "- PARENT"; return; fi
156
+ describe_dir() {
157
+ local dir="$1" ver ver_ok=0 code_ok=0
139
158
  ver="$(node -e "process.stdout.write(String(require('$dir/package.json').version||'0.0.0'))" 2>/dev/null || echo '0.0.0')"
140
159
  version_ge "$ver" "$MIN_FIXED_VERSION" && ver_ok=1
141
160
  if [ -f "$dir/dist/agent/runner.js" ]; then
142
- stripped="$(strip_js_comments "$dir/dist/agent/runner.js")"
143
- if printf '%s' "$stripped" | grep -q 'tools: \[\]' \
144
- && printf '%s' "$stripped" | grep -q 'canUseTool:'; then code_ok=1; fi
161
+ if strip_js_comments "$dir/dist/agent/runner.js" | has_tool_boundary; then code_ok=1; fi
145
162
  fi
146
163
  if [ "$ver_ok" = 1 ] && [ "$code_ok" = 1 ]; then echo "$ver FIXED"
147
164
  elif [ "$ver_ok" = 0 ]; then echo "$ver UNFIXED(v<$MIN_FIXED_VERSION)"
148
165
  else echo "$ver UNFIXED(code)"; fi
149
166
  }
150
167
 
168
+ # Prints "<version> <FIXED|UNFIXED(reason)|PARENT>" for a pid.
169
+ describe_pid() {
170
+ local dir
171
+ dir="$(pkg_dir_for_pid "$1")"
172
+ # No package dir = the `npm exec` wrapper, not a daemon. Its child is graded separately;
173
+ # counting the parent as "unknown, therefore exposed" cried wolf on every healthy npx
174
+ # launch, which is the mode that becomes correct once the fix is published.
175
+ if [ -z "$dir" ] || [ ! -f "$dir/package.json" ]; then echo "- PARENT"; return; fi
176
+ describe_dir "$dir"
177
+ }
178
+
179
+ # --grade <dir>: grade one package directory and touch no process. The test suite runs
180
+ # this against a fresh compile of runner.ts, so a change the check above stops recognising
181
+ # fails a test instead of killing every user's daemon on their next restart.
182
+ if [ "$MODE" = "--grade" ]; then
183
+ grade_dir="$(cd "$2" && pwd)"
184
+ read -r ver state <<<"$(describe_dir "$grade_dir")"
185
+ echo "$ver $state"
186
+ if [ "$state" = "FIXED" ]; then exit 0; fi
187
+ exit 1
188
+ fi
189
+
151
190
  # --status: the one command that answers "is my machine exposed right now?".
152
191
  if [ "$MODE" = "--status" ]; then
153
192
  exposed=0 total=0
@@ -0,0 +1,163 @@
1
+ ---
2
+ description: Refine the typed relations (Related Notes) of a SET of Noesis notes so the Graph view shows real structure. Scopes by timeframe, topic, catalog or signal — "last two weeks", "notes about BTC", "BTO printing in the last month", "points over 100". Proposes a diff per note and writes only on approval. Use when the user says 'refine relations', 'refine note relations', 'fix my graph', 'link my notes', 'these notes have no relations', or names a batch of notes to connect.
3
+ argument-hint: "[recent <N> days|weeks|months] [topic <words>] [catalog <NAME>] [points over <N>] [<note path | note ID | query>]"
4
+ ---
5
+
6
+ # /noesis-refine-relations — Batch relation refinement
7
+
8
+ You are executing `/noesis-refine-relations`. It selects a **set** of notes, works out which
9
+ other notes each one should formally link to, shows you the diff, and writes only what you
10
+ approve.
11
+
12
+ Relations live in the Noesis **database**, not in the markdown. This skill therefore never
13
+ edits a note body and never needs a sync.
14
+
15
+ ## What this is NOT
16
+
17
+ The app already has a per-note **"Suggest relations"** in the graph's node menu. It reasons
18
+ over ≤20 candidates carrying only a title and a short description — **it never reads note
19
+ bodies**, and it runs on one note at a time.
20
+
21
+ This skill is the complement: many notes at once, and it *reads the content*, so it can find
22
+ the links a note states in its own text (cited file paths, note URLs, quoted titles). Where
23
+ the in-app suggester is the right tool for one note, say so and stop.
24
+
25
+ ## Step 0 — Route on the argument
26
+
27
+ The raw arguments string is: `$ARGUMENTS`
28
+
29
+ First match wins:
30
+
31
+ | Argument looks like | Mode |
32
+ |---|---|
33
+ | "last N days/weeks/months", "recent", "this week" | **RECENT** |
34
+ | a topic phrase *and* a timeframe | **TOPIC ∩ RECENT** |
35
+ | a topic phrase ("about BTC", "BTO printing") | **TOPIC** |
36
+ | "points over N", "score above N", "favorite", "pinned", "starred" | **SIGNAL** |
37
+ | "catalog X" | **CATALOG** |
38
+ | a note ID, a `.md` path, or a bare query | **SINGLE** |
39
+ | empty | **ASK** — `AskUserQuestion` offering the modes above |
40
+
41
+ State the mode and the resolved parameters before doing anything.
42
+
43
+ ## Step 1 — Resolve the note set
44
+
45
+ - **RECENT** — `mcp__noesis__list_notes(recent: <N_DAYS>, limit: 100)`.
46
+ - **TOPIC** — `mcp__noesis__search_semantic(query, limit: 20)` **and**
47
+ `mcp__noesis__search_notes(query, limit: 50)`; union by ID.
48
+ - **TOPIC ∩ RECENT** — run both of the above and intersect.
49
+ - **SIGNAL** — `list_notes(limit: 100)`, then filter client-side on the `⭐` and `[N pts]`
50
+ markers in the output.
51
+ - **CATALOG** — `list_notes(catalog: <NAME>, limit: 100)`.
52
+ - **SINGLE** — resolve directly (`get_note` by id/path, or `search_notes` then pick).
53
+
54
+ Then **always**:
55
+
56
+ 1. **Drop capture notes** — anything under a `captures/` folder is an auto-pushed session
57
+ mirror, not a note the user authored. Never link them unless explicitly asked.
58
+ 2. **Cap the batch.** Default to 15 notes. If the scope resolved to more, take the 15 most
59
+ recently modified and **say how many you dropped** — never present a truncated sweep as
60
+ a complete one.
61
+ 3. Print the candidate table (ID, title, path, current relation count) and confirm the set
62
+ before moving on.
63
+
64
+ ### Selection limits you must respect
65
+
66
+ These are properties of the MCP tools, not preferences:
67
+
68
+ - `list_notes` caps `limit` at **100** server-side and exposes no offset. A scope wider than
69
+ 100 notes cannot be enumerated in one call — narrow it and say so.
70
+ - When `recent` is set, `root` and `catalog` are **silently ignored**. Combined filters must
71
+ be applied client-side.
72
+ - `list_notes` prints `⭐` and `[N pts]` but **not the note ID**, so resolve IDs from paths
73
+ before writing anything.
74
+ - `search_semantic` and `find_similar_notes` need embeddings and fail without a Gemini key.
75
+ Degrade to keyword-only and note it; never abort the run over it.
76
+
77
+ ## Step 2 — Per note: build the proposed set
78
+
79
+ For each note in the confirmed set, in order:
80
+
81
+ **2a. Read the current relations.** `mcp__noesis__get_note(id)`. This is the only reliable
82
+ read — use it every time, even when you expect none.
83
+
84
+ **2b. Gather candidates**, in one parallel batch:
85
+ - `mcp__noesis__find_similar_notes(note_id, limit: 15)`
86
+ - `mcp__noesis__search_semantic(query: title + description, limit: 10)`
87
+ - **The body scan — the highest-precision source, and the reason this skill exists.**
88
+ Read the note content and extract every note it already cites:
89
+ - file paths ending in `.md` (absolute, `~/Noesis/...`, or bare filenames)
90
+ - `noesisbrain.com/notes/<id>` URLs — the `<id>` is the target, use it directly
91
+ - quoted note titles
92
+ Resolve each to an ID with `mcp__noesis__search_notes`. An edge grounded in a line of the
93
+ note's own text is worth more than any similarity score; keep the line as evidence.
94
+
95
+ **2c. Filter.** Drop captures, drop the note itself (the MCP write path does **not** reject
96
+ self-links), drop anything that fails to resolve. Never invent an ID — if a cited note cannot
97
+ be resolved, report it as unresolved rather than guessing.
98
+
99
+ **2d. Choose a type** per edge, from exactly these five:
100
+ `related` · `references` · `implements` · `extends` · `supersedes`.
101
+ Default to `references` when the note merely cites the target, `related` when the connection
102
+ is thematic.
103
+
104
+ **2e. Union with the existing set from 2a.** Keep every existing entry unless the user has
105
+ explicitly asked to prune. This matters — see the write contract below.
106
+
107
+ **2f. Apply a cap.** 2–5 relations for a focused note; up to 8 for a comprehensive
108
+ architecture, workflow, or strategy note. **Tracker, sprint and index notes are exempt** —
109
+ being a hub is their job, and a tracker citing 25 notes should link to 25.
110
+
111
+ ## Step 3 — Propose
112
+
113
+ Print, per note:
114
+
115
+ ```
116
+ Note 2923 BTO 12.3 Aug Sprint 0 edges 0 -> 25
117
+ = keep references 4424 IDEA-1579 HTTPS support
118
+ + add references 3611 BPLAT-14016 UltraLite Simple Serialization
119
+ evidence: "my-git/issues/BPLAT-14016-phoenix-tsc-ultralite...md"
120
+ + add related 2924 Epsilon Branch Merge Convention
121
+ evidence: similar 0.81, both cover the Epsilon merge workflow
122
+ ! unresolved: "support-https-print-portal-investigation.md" (no match)
123
+ ```
124
+
125
+ Then ask once, for the whole batch, via `AskUserQuestion`: apply all / pick per note / cancel.
126
+ **Never write before this point.**
127
+
128
+ ## Step 4 — Write
129
+
130
+ For each approved note, issue **exactly one** `mcp__noesis__update_relations` call carrying
131
+ the **complete** desired set.
132
+
133
+ > **The write REPLACES the note's whole relation set.** It clears the column and stores what
134
+ > you send. One call per relation — a pattern that exists elsewhere in this repo's older
135
+ > commands — leaves the note with only the last relation written and silently deletes the
136
+ > rest. Send the union from 2e, always.
137
+
138
+ No inverse relation is created on the target, and you must not try to write one. Backlinks
139
+ are **derived**: the app scans every note's one-way array to build the graph, so a single
140
+ edge already shows up on both notes — as "linked to" on the source and "linked by" on the
141
+ target. Writing the mirror by hand would double-store derived data and turn every edge into
142
+ "mutual", destroying the direction the user can currently see.
143
+
144
+ Do **not** write a "Related Notes" section into the markdown body. Relations are database
145
+ metadata; a section in the file is a stale duplicate. If a previous run left one, remove it.
146
+
147
+ ## Step 5 — Report
148
+
149
+ A table of note · edges before → after · skipped-and-why, plus every unresolved citation.
150
+ Then suggest `mcp__noesis__get_relation_graph(note_id, depth: 1)` or the Graph view to
151
+ confirm the shape.
152
+
153
+ ## Implementation notes
154
+
155
+ - **Read before write, every time.** The write is a replace; skipping 2a loses relations.
156
+ - **The body scan is the differentiator.** If you skip it, this skill is just a slower
157
+ version of the in-app suggester.
158
+ - **Evidence or no edge.** Every proposed link needs either a quoted line from the note or a
159
+ similarity score. "These feel related" is not a reason.
160
+ - **Sequential per note.** Parallel `get_note` batches race the API rate limit; the
161
+ *candidate gathering* inside one note (2b) is the part to parallelize.
162
+ - **Report every cap.** A truncated batch, a dropped candidate, an unresolved citation — all
163
+ of it goes in the Step 5 report. A sweep that hides what it skipped is worse than no sweep.
@@ -0,0 +1,79 @@
1
+ ---
2
+ description: Work a Noesis Jot ticket (TCK-<n>) from this Claude Code session — bind it, link the running session to it, keep its status current, refine its text within the word limit, reset the binding, or list your tickets.
3
+ argument-hint: TCK-<n> | link TCK-<n> [status] [--force] | status [<status>] | refine | reset | list
4
+ ---
5
+
6
+ The Noesis Jot page is the user's quick-note board. Each ticket (TCK-<n>) shows a work status and which Claude Code session, on which computer, is working on it. This skill keeps that page true while you work.
7
+
8
+ The ticket tools (`mcp__noesis__backlog_bind`, `backlog_set_status`, `backlog_unbind`, `backlog_refine`, `backlog_get`) are bookkeeping on the user's own ticket, which the user asked for. Calling them in plan mode is expected.
9
+
10
+ ## Arguments
11
+
12
+ $ARGUMENTS
13
+
14
+ ## The status ladder
15
+
16
+ | Status | Set it when |
17
+ |---|---|
18
+ | `planning` | you start reading the ticket and the code, and writing a plan (plan mode) |
19
+ | `ready_to_implement` | your plan is presented and waits for the user's approval |
20
+ | `implementing` | the user approved the plan and you are building it |
21
+ | `iterating` | the first version is delivered; the user is testing it and asking for changes |
22
+ | `ready_to_merge` | you judge the work complete: every piece of feedback is done, or a `/cc-loop` reached LOOP COMPLETE. You wait for the user to say "ship it" |
23
+ | `deploying` | you are merging, pushing and verifying the deployment |
24
+ | `shipped` | the change is live in production |
25
+
26
+ `created` and `dropped` are the user's to set, on the page. Never skip a step you actually went through, and never claim a status you have not reached.
27
+
28
+ ## Sub-commands
29
+
30
+ ### `TCK-<n>` — start a ticket
31
+
32
+ 1. `backlog_get(key)` and read the ticket: the refined text if there is one, otherwise the draft.
33
+ 2. `backlog_bind(key)`. It sets `planning`.
34
+ 3. Work on it. Move the status up the ladder as you go.
35
+
36
+ ### `link TCK-<n> [status] [--force]` — link the session you are already in
37
+
38
+ Use it when this session is already working and the ticket was never handed over, or after `/clear`: you then no longer remember the ticket, and `link` tells you again which ticket it is and what status it has. (The binding keeps the session id the MCP server started with, which does not change on `/clear`.)
39
+
40
+ 1. With a status, bind with that status. Without one, pick the status that matches where this session really is:
41
+ - in plan mode → `planning`;
42
+ - a plan presented and waiting → `ready_to_implement`;
43
+ - an approved plan, and code being changed → `implementing`;
44
+ - a first version delivered, and the user giving feedback → `iterating`;
45
+ - a `/cc-loop` whose state file says COMPLETE → `ready_to_merge`.
46
+ 2. `backlog_bind(key, status)`. Pass `force` only when the user wrote `--force`.
47
+ 3. If the session has already made a plan, refine the ticket once (see `refine`).
48
+
49
+ If the ticket is bound to another session (`BOUND_ELSEWHERE`), tell the user which computer holds it and change nothing unless they wrote `--force`.
50
+
51
+ ### `status [<status>]`
52
+
53
+ "This session's ticket" is the one you bound earlier in this conversation, or the one the `tck-session` reminder names. If you are not sure which it is, ask.
54
+
55
+ Without a status, show the bound ticket's key, status, computer and session. With one, `backlog_set_status(key, status)`.
56
+
57
+ ### `refine`
58
+
59
+ Rewrite the ticket's text so it matches the agreed plan, and store it with `backlog_refine`. It must stay a jot:
60
+
61
+ - 70%–120% of the draft's word count (5 words of slack for short drafts; each Chinese, Japanese or Korean character counts as one word);
62
+ - the same language as the draft;
63
+ - plain sentences or a short list — no headings, no code fences, no increments, acceptance criteria or plan sections.
64
+
65
+ Refine once when your plan is ready, just before `ExitPlanMode`. Refine again only if the plan changes or the user asks. On `REFINE_LENGTH`, read `draft_words`, `min` and `max` from the error and rewrite to fit. The user's draft is never changed.
66
+
67
+ ### `reset`
68
+
69
+ `backlog_unbind(key)` for this session's ticket. The status stays as it is.
70
+
71
+ ### `list`
72
+
73
+ Call `backlog_get` with no filter, and keep the tickets whose status is in progress (`planning` to `deploying`). Show key, status, computer and the first line of each.
74
+
75
+ ## Rules
76
+
77
+ - One ticket per session. `SESSION_BUSY` means this session holds another ticket: switch (`switch: true`) only when the user clearly hands over the new one, never because a ticket is merely mentioned.
78
+ - `RESET_BY_OWNER` means the user reset this session's binding on the page. Bind again (`force: true`) only if the user asks.
79
+ - Never put the full plan on the ticket. Nobody reads the long version; the ticket stays a jot.
@@ -12,6 +12,13 @@
12
12
  - Note relations and codebase links live in the Noesis **database**, not in `.md` frontmatter — don't grep `.md` files for them.
13
13
  - Use `mcp__noesis__get_note` (read by ID or path), `mcp__noesis__find_similar_notes` (semantic similarity), `mcp__noesis__search_semantic` / `mcp__noesis__search_notes` (meaning / keyword search), `mcp__noesis__update_relations` (read-only when called without the `relations` param).
14
14
 
15
+ ### Noesis tickets (Jot page, TCK-<n>)
16
+ - The Jot page shows each ticket's work status and which session, on which computer, is working on it. Keep both true.
17
+ - When the user hands you a ticket (pastes `TCK-<n>: …`, or asks you to read, plan or work on TCK-<n>), bind it to this session with `mcp__noesis__backlog_bind`. The ticket tools are bookkeeping the user asked for, so calling them in plan mode is expected.
18
+ - Keep the status current with `mcp__noesis__backlog_set_status`: planning → ready_to_implement (plan presented) → implementing (plan approved) → iterating (first version delivered, user giving feedback) → ready_to_merge (you judge the work complete) → deploying (merging, pushing, verifying) → shipped (live). `created` and `dropped` are the user's.
19
+ - Refine the ticket text once, when your plan is ready, with `mcp__noesis__backlog_refine`: 70%–120% of the draft's words, in the draft's language. Never add increments, acceptance criteria or plan sections to a ticket — it stays a jot.
20
+ - The `noesis-ticket` skill covers start, `link` (bind the session you are already in, also after `/clear`), status, refine, reset and list.
21
+
15
22
  ### Path-as-identifier (cross-machine)
16
23
  For any path inside a Noesis-watched root, the path is a Noesis identifier — the cloud copy is always authoritative, and the local copy on this machine may be **missing** (other machine) or **stale** (the user edited the note via the Noesis web UI's Quick Fix; the web UI surfaces that state with the badge 「在线编辑 · 等待本地同步」). Either way, the local copy is not a trustworthy source of truth — reading it directly silently feeds outdated content into the rest of the turn.
17
24