Repository navigation
feat: workspace skills and knowledge first, a starter after linking, and the start-screen box back - #1435
Conversation
…refer The skill list the agent saw rendered a workspace skill, a project skill, a built-in and another team's copy identically. With near-duplicates in the catalog it asked "which team's policy?" or explored the repo instead of loading the workspace's own skill. - Every registered skill gets a `source` (`workspace`, `project`, `built-in`, `personal`, `other`), decided from its location (`altimate/skill-source.ts`). It is on `Skill.Info`, so `/skill` returns it too. - Both listings render it as `<skill source="…">`; a description cannot forge one, since `skill` is already a neutralised tag. - The skills preamble states the order: workspace, project, built-in, personal, other; prefer the highest that fits and do not ask which team's skill to use. - Listings stay in name order. Ordering by source moved the built-ins behind every project skill and out of the skill tool's 50-entry list, and they were loaded less (12/30 against 18/30). Measured with the routing harness against a real workspace snapshot and 150 other-team near-duplicates in `.claude/skills`: workspace asks 11/11 (0.12.6: 8/12), built-in asks 18/30 (0.12.6: 18/30), no built-in ask loaded a workspace skill. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018fJ3X7pcGT4R9yzjsJnqsV
…eybindings 0.12.5's workspace box under "What is Altimate Code" left every keybinding (Ctrl+C, Ctrl+P, Esc) dead, and 0.12.6 took it out. Bisected with builds of the box: the binding lookup and the polling were fine; the view's root was a `<Show>` that renders nothing until the binding resolves. OpenTUI's `Slot` shows the fallback for a view whose first render is empty and renders the view again when its output changes, so the lines arriving mounted a new view that started empty and resolved again: the lines never stayed and the box remounted without end. - `WelcomeBlock` keeps a `<box>` root, and the box is registered again. - Tests mount the real slot machinery with lines that arrive after mount: the block shows them and mounts once; a bare `<Show>` root is pinned as remounting, so an OpenTUI change is noticed. Checked in tmux on a linked project: the lines show and Ctrl+P opens the command palette. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018fJ3X7pcGT4R9yzjsJnqsV
…used unasked After a link the user got "Done." and nothing about what the workspace brings. And the agent searched workspace knowledge only when told to. - `altimate/workspace/starter.ts` renders "You're working in the X workspace", its skills, knowledge, integrations and memory setting, and up to three prompts. Built from the same data as the prompt's workspace section, with no model call; anything not known is left out. - `altimate-code link` prints it after every successful link (a framed note in a terminal, plain lines otherwise). - TUI: the post-link dialog shows the summary and offers the prompts; choosing one fills the input without sending it. `/workspace` gains **Get started**. The start-screen box points to it. - `GET /altimate/workspace/starter` serves it to the IDE extension's chat: `linked: false` for an unlinked project, 503 for a link that could not be confirmed, same origin rules as the other workspace routes. - The workspace section tells the agent to check the workspace's knowledge (`ask_knowledge_base`) for best practices and conventions without being asked, when the workspace has knowledge. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018fJ3X7pcGT4R9yzjsJnqsV
|
Note Currently processing new changes in this PR. This may take a few minutes, please wait... ⚙️ Run configuration
📒 Files selected for processing (24)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
- The starter's "skills not synced" line names `workspace refresh`, which pulls skills; it said `workspace sync`, which sends memory. - The post-link TUI confirmation opens at once and the starter fills in when it arrives. It used to wait up to ~12s with nothing on screen. - That wait joins only a skill sync already running (`pendingSync`); it no longer starts a second one when the link's sync has finished. - `workspace` is now only this project's own snapshot, judged on both the matched and the resolved path. A configured path into another project's snapshot, or a symlink into or out of this one, is `other`. - The built-in check anchors installed copies to `<home>/.altimate/builtin`, and skill-use telemetry shares the same predicate. - Integration ids go through `clean()` now that the starter prints them to the terminal; `source` goes through `escapeSkillAttr`. - Docs: a project skill with exactly a workspace skill's name replaces it. - The start-screen box follows a link or unlink made in this process at once, instead of saying "not linked" under the link confirmation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018fJ3X7pcGT4R9yzjsJnqsV
ralphstodomingo
left a comment
There was a problem hiding this comment.
Requesting changes for one thing: when a session runs from a subdirectory of a git repo, the workspace's own skills get the label other. That is the lowest source, so the new rule tells the agent to prefer everything else over them. The rest holds up, including the welcome-box fix.
What I checked at cb94222b
- The welcome box. I read the
Slotin OpenTUI 0.3.4.renderEntrychecks the view's first output inside the entry's memo. A<Show>root that fills in later makes that memo run again, which calls the plugin's renderer and mounts a new, empty view. A<box>root gives the memo nothing to re-run on.workspace-welcome-slot.test.tsxruns the real slot code with both roots. - The labels can't be forged.
sourcecomes from a fixed list, is set from where discovery found the file, and is escaped in the listing.classifySkillSourcenow uses the same built-in check, so telemetry and the listing agree. - The starter after a TUI link waits on the bind's sync under the same realpath key the bind uses, so
pendingSyncfinds it. The raw-client call follows the patternskill-opsalready uses. - The 9 test files this PR touches pass locally (263 tests), the Marker Guard passes, and CI is green.
Blocking: in a subdirectory session, workspace skills are labelled other (skill-source.ts:60, inline). link records the project at the directory it runs in (resolveProjectIdentifier returns the realpath of that directory), and the prompt's sync writes the snapshot under Instance.directory. But skillSource counts a snapshot as the workspace's only when it belongs to projectRoot, and that is the git worktree. Take a project in repo/dbt, linked from there: the snapshot is at repo/dbt/.altimate-code/skill/_workspace, and every workspace skill is labelled other.
I checked this with the branch's own Skill.Service, using the fixture from "each skill is labelled with where it was found" with the session in repo/sub:
- Linked at the repo root, the workspace skill is
workspace. - Linked at
repo/sub, it isother, while the repo's.claude/skillsskill isproject, so the rule ranks the repo's skill above the workspace's.
For this audience, a dbt project in a subfolder of a larger repo is a common layout. Counting the session directory as an owner as well fixes it (diff inline). With that change both cases come out workspace, and skill-source.test.ts, skill.test.ts and system.test.ts still pass.
Non-blocking
- The selection jumps when the starter arrives (
workspace.tsx:435, inline). - The
Skill.statefactory is now at cognitive 26, up from 23. The labelling loop could move intoskill-source.tsas one call, which would also shrink the diff in this upstream-shared file.
Appendix — complexity delta
b9173a2dd9 → cb94222b40 · only functions this diff touches · advisory, not a gate.
| Function | File | Cognitive | CCN | Status |
|---|---|---|---|---|
Skill.state factory L436 |
src/skill/index.ts |
23 → 26 ▲ | 14 → 16 ▲ | hard to follow → needs decomposition |
skillSource L51 |
src/altimate/skill-source.ts |
new → 9 | new → 17 | new ≥15 CCN — easy |
summaryOf L80 |
src/altimate/workspace/starter.ts |
new → 10 | new → 10 | new — easy |
title L394 |
src/plugin/tui/altimate/workspace.tsx |
2 → 4 ▲ | 4 → 8 ▲ | trivial |
bindOrRebind L768 |
src/cli/cmd/link.ts |
41 = | 24 → 26 ▲ | needs decomposition (unchanged) |
runBrowserHandoff L441 |
src/cli/cmd/link.ts |
15 = | 15 → 17 ▲ | moderate |
classifySkillSource L26 |
src/tool/skill.ts |
2 = | 6 → 3 ▼ | trivial |
Summary: 32 touched · 12 rose · 4 improved · 31 new (max cognitive 10) · net cognitive Δ +86. Some rows are left out because the tool assigns their scores to the wrong function: rebindByMatchedIdentifier (5 → 20, CCN 286) and handoffFailureMessage, which this PR doesn't touch, plus the anonymous-callback rows other than Skill.state.
| if (matchedProject !== null || realProject !== null) { | ||
| const ours = (project: string | null, root: string | undefined) => | ||
| project !== null && root !== undefined && path.resolve(project) === path.resolve(root) | ||
| return ours(matchedProject, ctx.projectRoot) && ours(realProject, ctx.realProjectRoot ?? ctx.projectRoot) |
There was a problem hiding this comment.
A snapshot counts as the workspace's only when it belongs to projectRoot, which Skill.state sets to the git worktree. But link records the project at the directory it runs in, and the prompt's sync writes the snapshot under Instance.directory. So a project linked from repo/dbt has its snapshot at repo/dbt/.altimate-code/skill/_workspace, and every skill in it is labelled other. The rule then ranks the repo's own .claude/skills above it.
I reproduced this with the "each skill is labelled with where it was found" fixture, with the session in repo/sub and the snapshot written there: workspace-one came out other and project-one came out project.
Counting the session directory as an owner as well fixes it. With this change both cases are workspace, and the existing tests still pass:
// SkillSourceContext: directory?: string; realDirectory?: string
const ours = (project: string | null, roots: (string | undefined)[]) =>
project !== null && roots.some((root) => root !== undefined && path.resolve(project) === path.resolve(root))
return ours(matchedProject, [ctx.projectRoot, ctx.directory]) &&
ours(realProject, [ctx.realProjectRoot ?? ctx.projectRoot, ctx.realDirectory ?? ctx.directory])
? "workspace"
: "other"In Skill.state, pass directory: ctx.directory and realDirectory: resolve(ctx.directory). A test like the one in skill.test.ts, with the session and snapshot in a subdirectory, would pin it.
There was a problem hiding this comment.
Fixed in fef6096. The session directory now counts as an owner of the snapshot alongside the project root, as you suggested (skillSource takes directory/realDirectory, passed from ctx.directory in Skill.state). Added your case to skill.test.ts (session and snapshot in sub/: workspace-one → workspace, project-one → project); it fails without the change.
| title={title()} | ||
| options={options()} | ||
| current={props.manageUrl ? "open" : "done"} | ||
| current={props.starter()?.prompts.length ? "prompt:0" : props.manageUrl ? "open" : "done"} |
There was a problem hiding this comment.
Non-blocking. DialogSelect moves the selection whenever current changes (dialog-select.tsx:228), and the prompts are added above the existing rows. The starter can take a few seconds to arrive: up to 5 s for the sync, then the route's own waits. If someone has already moved to "Continue editing in browser" by then, the cursor jumps to the first prompt, so pressing Enter fills the input instead of opening the browser. The selection is tracked by row index, so adding rows above it shifts it too. Moving to the first prompt only when the user hasn't moved yet would avoid both.
There was a problem hiding this comment.
Fixed in fef6096, and reproduced first: moving to Done before the starter arrived, then Enter, filled the input. Two changes: the prompts now go below the existing rows, so a late arrival can't shift the index-tracked selection; and the cursor jumps to the first prompt only if the user hasn't moved off the default row. That also needed current as a createMemo, because the dialog re-applies current on every notification and a plain getter pulled the cursor back as soon as the user moved. Checked in tmux: moved → Enter on Done closes with an empty input; not moved → it lands on the first prompt.
…ace's Review on the starter and skill-label change: - A project linked from a subdirectory of its repository keeps its snapshot under the session directory, while the registry takes the git worktree as the project. Its skills were labelled `other`, the lowest source. The session directory now also counts as an owner of the snapshot. - The post-link dialog no longer pulls the cursor off a row the user moved to before the starter arrived. The prompts are added below the existing rows, so a late arrival cannot shift the selection, and `current` is a memo, because the dialog re-applies it on every notification. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018fJ3X7pcGT4R9yzjsJnqsV
ralphstodomingo
left a comment
There was a problem hiding this comment.
Approving at fef6096f. Both points from my last review are fixed.
- A subdirectory session's snapshot is now labelled
workspace.skillSourcecounts the session directory as an owner as well as the project root, on both the matched and the resolved path. The newskill.test.tscase runs the real registry with the session and snapshot insub/. When I removed the directory from the owners, it failed, along with the newskill-source.test.tscase. - The dialog no longer moves the cursor under the user. The prompts now come after Done.
DialogSelectgroups rows by category in insertion order, so they land below the existing rows and can't shift the index-tracked selection. Makingcurrenta memo is the right call.onMovesetsmovedEarly, and with a plain getter that change would have re-fired the dialog'son(() => props.current)effect and pulled the cursor back to the default row. The programmatic moves don't trip it: the first move is to the default row, and the jump to the first prompt happens after the starter has arrived.
The 9 test files this PR touches pass locally (265 tests), the Marker Guard passes, and CI is green.
Appendix — complexity delta
b9173a2dd9 → fef6096f61 · only functions this diff touches · advisory, not a gate.
| Function | File | Cognitive | CCN | Status |
|---|---|---|---|---|
Skill.state factory L436 |
src/skill/index.ts |
23 → 26 ▲ | 14 → 16 ▲ | hard to follow → needs decomposition |
skillSource L56 |
src/altimate/skill-source.ts |
new → 9 | new → 18 | new ≥15 CCN — easy |
summaryOf L80 |
src/altimate/workspace/starter.ts |
new → 10 | new → 10 | new — easy |
title L394 |
src/plugin/tui/altimate/workspace.tsx |
2 → 4 ▲ | 4 → 8 ▲ | trivial |
bindOrRebind L768 |
src/cli/cmd/link.ts |
41 = | 24 → 26 ▲ | needs decomposition (unchanged) |
runBrowserHandoff L441 |
src/cli/cmd/link.ts |
15 = | 15 → 17 ▲ | moderate |
classifySkillSource L26 |
src/tool/skill.ts |
2 = | 6 → 3 ▼ | trivial |
Summary: 34 touched · 12 rose · 6 improved · 32 new (max cognitive 10) · net cognitive Δ +84. As in the last round, rebindByMatchedIdentifier and handoffFailureMessage are left out because the tool assigns other functions' scores to them (this PR doesn't touch either), and so are the anonymous-callback rows other than Skill.state.
|
🔁 @sahrizvi — this change was promoted to the help-docs hub: https://github.com/AltimateAI/help-docs/pull/119 — please review/merge to keep the docs in sync. |
Code Review SummaryThe review did not run because the selected model is no longer available. Choose another model in Kilo Code review settings: https://app.kilo.ai/code-reviews |
Issue for this PR
No GitHub issue; tracked internally.
Type of change
What does this PR do?
Three commits.
1. Skill source labels and a precedence rule.
source="workspace|project|built-in|personal|other", set from where altimate-code found it (altimate/skill-source.ts).2. The start-screen workspace box is back.
<Show>that renders nothing until the binding resolves. OpenTUI'sSlotshows the fallback for an empty first render and renders the view again when its output changes, so each arrival mounted a fresh view that started empty and resolved again. The lines never stayed and the box remounted without end.WelcomeBlockkeeps a<box>root.<Show>root (todo, files, mcp) read shared state, so they remount once rather than loop; I left them alone.3. A fixed-format workspace starter, and knowledge used without being asked.
altimate-code link, in the TUI's post-link dialog, and from a new/workspace→ Get started. Picking a prompt fills the input without sending it.GET /altimate/workspace/starterserves it for the IDE extension's chat. The extension still needs to call it.ask_knowledge_base) for best practices and conventions unprompted.How did you verify your code works?
<box>root, and so on.origin/mainand they fail there too..claude/skills, usingaltimate-backend/altimate-default.ask_knowledge_basecalled unprompted on 4/4 best-practice questions, against 0/4 on 0.12.6 with the tool available.linkprint the starter;/workspace→ Get started works;Screenshots / recordings
Terminal captures from tmux are in the verification notes above. No recording.
Checklist
🤖 Generated with Claude Code
https://claude.ai/code/session_018fJ3X7pcGT4R9yzjsJnqsV
Summary by cubic
Makes the linked workspace's own skills and knowledge win over near-duplicates and general advice, shows a fixed starter message after linking, and brings back the start-screen workspace box without the dead keybindings that took it out in 0.12.6.
Skills: sources and precedence
source="workspace|project|built-in|personal|other", set from wherealtimate-codefound it.workspaceis only this project's own snapshot, judged on both the matched and resolved path; a session started in a subdirectory of the repository still owns its snapshot there. A symlink or configured path into another project's snapshot isother, and a project skill with the exact name of a workspace skill replaces it.Starter, knowledge, and the welcome box
/workspace→ Get started show the workspace's skills, knowledge, integrations, memory setting, and up to three prompts; the dialog opens at once and the starter fills in when it arrives. Prompts land below the existing rows, so a late arrival doesn't move the selection, and choosing one fills the input without sending it.GET /altimate/workspace/starterserves the same message to the IDE extension's chat, which still needs to call it.<box>root, which stops OpenTUI from remounting it endlessly and killing keybindings, and it now refreshes right away on an in-process link or unlink.Written for commit fef6096. Summary will update on new commits.