Skip to content

Feature/profile messaging#64

Merged
mohanagy merged 4 commits into
mainfrom
feature/profile-messaging
May 8, 2026
Merged

Feature/profile messaging#64
mohanagy merged 4 commits into
mainfrom
feature/profile-messaging

Conversation

@mohanagy
Copy link
Copy Markdown
Owner

@mohanagy mohanagy commented May 8, 2026

Summary

Testing

  • npm run test:run
  • npm run typecheck
  • npm run build
  • npm pack --dry-run (if packaging or install behavior changed)

Checklist

  • I updated docs for any user-visible change
  • I added or updated tests when behavior changed
  • I did not commit secrets, private corpora, or accidental generated artifacts
  • I kept this PR focused on a single change or tightly related set of changes

Related issues

Summary by CodeRabbit

  • New Features

    • Added optional --profile core|full flag for MCP installs (claude, cursor, copilot) and updated CLI help to show it.
    • Copilot MCP available as a first-class install/uninstall action.
    • Context expansion now includes real source snippets, prefers explicit line numbers, and omits out-of-workspace snippets.
  • Documentation

    • Updated README quickstart and examples to show --profile full install option.

mohanagy and others added 2 commits May 8, 2026 20:15
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 8, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds selectable MCP tool profiles (core vs full) via CLI flags and environment configuration, extends candidate metadata throughout the context-pack pipeline with source location and snippet information, and implements focused snippet retrieval during context expansion.

Changes

MCP Tool Profiles & CLI System

Layer / File(s) Summary
Profile Types
src/infrastructure/install.ts
Introduces MCP_TOOL_PROFILES constants, McpToolProfile type, and isMcpToolProfile type guard for validating profile selections.
CLI Parser
src/cli/parser.ts
Updates platform action parser with PROFILE_AWARE_PLATFORM_COMMANDS and parsePlatformActionArgs to recognize and validate --profile core|full for claude, cursor, and copilot commands.
CLI Main Integration
src/cli/main.ts
Adds installCopilotMcp as a dependency-injected function, updates CliDependencies, and routes parsed profile arguments to install functions for claude, cursor, and copilot.
Install Function Signatures
src/infrastructure/install.ts
Updates claudeInstall, cursorInstall, and installCopilotMcp to accept McpInstallOptions and thread profile selection into installMcpServer environment configuration.

Candidate Metadata & Coverage Refactoring

Layer / File(s) Summary
Candidate Metadata Schema
src/runtime/context-pack.ts
Extends ContextPackNodeCandidate with optional fields: source_file, line_number, file_type, node_kind, snippet; introduces CoverageNodeCandidate and CoverageEntry for lightweight coverage computation.
Coverage Pipeline Refactoring
src/runtime/context-pack.ts
Refactors coverageEntriesForCandidates, semantic classification helpers (isTestEntry, isConfigurationEntry, isContractEntry, isImplementationEntry), and compileContextPack to work with projected entry shapes without materializing omitted entries.
Metadata Hydration
src/runtime/pr-impact.ts, src/runtime/retrieve.ts
Precomputes normalized source path serialization outside lazy closures and emits candidate metadata fields; retrieve conditionally includes snippet only when storedSnippet exists.
Focused Snippet Retrieval
src/runtime/stdio/tools.ts
Implements snippetSourcePathCandidates and readFocusedSnippet utilities to resolve and read source snippets during focused context expansion with per-expansion file caching.

Documentation & Tests

Layer / File(s) Summary
Documentation
README.md, examples/why-graphify.md
Adds explicit --profile full commented examples and clarifies that full 25-tool surface is opt-in via --profile full or GRAPHIFY_TOOL_PROFILE=full; fixes code-fence placement in README.
Unit Tests
tests/unit/cli.test.ts, tests/unit/install.test.ts, tests/unit/context-pack.test.ts, tests/unit/stdio-server.test.ts, tests/unit/why-graphify-doc.test.ts
Adds and updates tests for profile parsing/validation, install/uninstall with profiles (including Copilot), candidate materialization behavior, focused snippet inclusion and scope, and README doc assertion.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I hopped through flags and files today,

Core whispers neat, full invites the play.
Candidates now wear path and line like thread,
Snippets fetched where focused pointers led.
Docs and tests sing which profile to say.

🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (2 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is entirely a template with empty placeholder sections and unchecked checkboxes; it provides no substantive summary of changes, testing performed, or context about the work. Complete all required sections: add a concrete summary of what was changed and why, document how testing was performed, and check off applicable checklist items before merging.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Feature/profile messaging' is vague and does not clearly communicate the main changes, using a generic prefix pattern that lacks specificity about what profile messaging was implemented. Consider a more descriptive title like 'Add MCP tool profile support for MCP server installations' or 'Support --profile option for claude/cursor/copilot installs' to clearly convey the primary change.
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/profile-messaging

Comment @coderabbitai help to get the list of available commands and usage tips.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/cli/main.ts (1)

853-863: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Copilot uninstall leaves MCP server config behind

copilot install now registers MCP config, but copilot uninstall only removes the skill. Users end up with a partial uninstall and stale .vscode/mcp.json Graphify entry.

Suggested fix direction
if (command === 'copilot') {
  const options = parsePlatformActionArgs(command, args)
  if (options.action === 'install') {
    io.log(dependencies.installSkill('copilot'))
    io.log(dependencies.installCopilotMcp('.', options.profile ? { profile: options.profile } : {}))
  } else {
    io.log(dependencies.uninstallSkill('copilot'))
+   io.log(dependencies.uninstallCopilotMcp('.'))
  }
  return 0
}

Also add uninstallCopilotMcp to src/infrastructure/install.ts and CliDependencies with tests.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/cli/main.ts` around lines 853 - 863, The uninstall path for the 'copilot'
command only removes the skill but leaves MCP server config (e.g.
.vscode/mcp.json / Graphify entry) behind; add a new uninstallCopilotMcp method
to the install module and to the CliDependencies interface, implement it in
src/infrastructure/install.ts to remove MCP config/Graphify entries (mirror
installCopilotMcp behavior in reverse), call
dependencies.uninstallCopilotMcp('.') inside the else branch in src/cli/main.ts
when action !== 'install', and add unit tests that verify uninstallCopilotMcp is
invoked and that .vscode/mcp.json and Graphify entries are removed.
🧹 Nitpick comments (1)
tests/unit/cli.test.ts (1)

1532-1546: ⚡ Quick win

Add Copilot profile pass-through coverage in this test

This test locks Claude/Cursor profile forwarding but not Copilot, even though Copilot is now profile-aware too. Please assert installCopilotMcp('.', { profile: 'full' }) for copilot install --profile full.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unit/cli.test.ts` around lines 1532 - 1546, The test currently verifies
profile forwarding for Claude and Cursor but misses Copilot; add a call to
executeCli(['copilot', 'install', '--profile', 'full'], io, dependencies) and
assert the mock installCopilotMcp was called with the same args
(installCopilotMcp('.', { profile: 'full' })). Create a mock for
installCopilotMcp on the dependencies object (similar to
claudeInstall/cursorInstall), hook it into dependencies before invoking
executeCli, and add an expectation using toHaveBeenCalledWith to verify profile
pass-through for Copilot; reference executeCli, createDependencies/createIo, and
installCopilotMcp to locate the changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/runtime/context-pack.ts`:
- Around line 529-554: coverageEntryForCandidate currently returns early when
candidate.source_file exists, skipping candidate.build_entry() and leaving
entry.snippet null; that causes isConfigurationEntry (which checks entry.snippet
for inline env detection) to misclassify nodes. Fix by not short-circuiting
solely on source_file — call candidate.build_entry() (or at least materialize
snippet) when snippet is missing or when isConfigurationEntry needs the snippet;
i.e., in coverageEntryForCandidate, if candidate.source_file exists but
candidate.snippet is falsy, invoke candidate.build_entry() and use its values
for entry.snippet/file_type/node_kind so isConfigurationEntry sees the real
snippet.

In `@src/runtime/stdio/tools.ts`:
- Around line 425-430: The expanded-node construction in context_expand (in
src/runtime/stdio/tools.ts) currently always uses
lineNumberFromSourceLocation(...) to set line_number and snippet, which
overrides an explicitly provided line_number and yields line_number: 0/snippet:
null for graphs that supply line_number without source_location; change the
logic to prefer the explicit stored.follow_up.line_number when it is present and
non-zero (i.e., use stored.follow_up.line_number as the line_number and use the
corresponding snippet lookup/read routine) and only fall back to
lineNumberFromSourceLocation(...) when the explicit line_number is absent/zero;
apply the same fix to the other similar block around the 449-467 range so both
expansions honor an explicit line_number and produce the correct snippet.
- Around line 245-276: snippetSourcePathCandidates/readFocusedSnippet allow
unsafe paths: change the graphDir detection to a platform-safe check (e.g.
compare path.basename(graphDir) === 'graphify-out' instead of endsWith), remove
or stop returning a raw resolve(sourceFile) that can escape the workspace, and
ensure every candidate is resolved and then gated to stay inside the computed
workspace (projectDir or graphDir) by using path.resolve + path.relative to
reject candidates that traverse outside the workspace; update
snippetSourcePathCandidates to only return unique, workspace-contained absolute
paths and leave readFocusedSnippet to iterate those safe candidates.

---

Outside diff comments:
In `@src/cli/main.ts`:
- Around line 853-863: The uninstall path for the 'copilot' command only removes
the skill but leaves MCP server config (e.g. .vscode/mcp.json / Graphify entry)
behind; add a new uninstallCopilotMcp method to the install module and to the
CliDependencies interface, implement it in src/infrastructure/install.ts to
remove MCP config/Graphify entries (mirror installCopilotMcp behavior in
reverse), call dependencies.uninstallCopilotMcp('.') inside the else branch in
src/cli/main.ts when action !== 'install', and add unit tests that verify
uninstallCopilotMcp is invoked and that .vscode/mcp.json and Graphify entries
are removed.

---

Nitpick comments:
In `@tests/unit/cli.test.ts`:
- Around line 1532-1546: The test currently verifies profile forwarding for
Claude and Cursor but misses Copilot; add a call to executeCli(['copilot',
'install', '--profile', 'full'], io, dependencies) and assert the mock
installCopilotMcp was called with the same args (installCopilotMcp('.', {
profile: 'full' })). Create a mock for installCopilotMcp on the dependencies
object (similar to claudeInstall/cursorInstall), hook it into dependencies
before invoking executeCli, and add an expectation using toHaveBeenCalledWith to
verify profile pass-through for Copilot; reference executeCli,
createDependencies/createIo, and installCopilotMcp to locate the changes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7bdf6ca3-4dd3-43a3-9d39-298ca8a6d963

📥 Commits

Reviewing files that changed from the base of the PR and between ea60123 and b147f66.

📒 Files selected for processing (14)
  • README.md
  • examples/why-graphify.md
  • src/cli/main.ts
  • src/cli/parser.ts
  • src/infrastructure/install.ts
  • src/runtime/context-pack.ts
  • src/runtime/pr-impact.ts
  • src/runtime/retrieve.ts
  • src/runtime/stdio/tools.ts
  • tests/unit/cli.test.ts
  • tests/unit/context-pack.test.ts
  • tests/unit/install.test.ts
  • tests/unit/stdio-server.test.ts
  • tests/unit/why-graphify-doc.test.ts

Comment thread src/runtime/context-pack.ts
Comment thread src/runtime/stdio/tools.ts
Comment thread src/runtime/stdio/tools.ts
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/cli/main.ts (1)

858-867: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Prevent partial Copilot state when one of the two mutations fails.

At Line 862–Line 866, install/uninstall executes two side-effecting operations in sequence with no rollback/compensation. If the second step fails, the workspace is left half-configured.

💡 Suggested hardening
 if (command === 'copilot') {
   const options = parsePlatformActionArgs(command, args)
   if (options.action === 'install') {
     if (!existsSync('graphify-out/graph.json')) {
       io.log("Warning: graphify-out/graph.json not found. Run 'graphify-ts generate .' first, then re-run this command.")
     }
     io.log(dependencies.installSkill('copilot'))
-    io.log(dependencies.installCopilotMcp('.', options.profile ? { profile: options.profile } : {}))
+    try {
+      io.log(dependencies.installCopilotMcp('.', options.profile ? { profile: options.profile } : {}))
+    } catch (error) {
+      io.log('Copilot MCP install failed; rolling back Copilot skill install.')
+      io.log(dependencies.uninstallSkill('copilot'))
+      throw error
+    }
   } else {
-    io.log(dependencies.uninstallCopilotMcp('.'))
-    io.log(dependencies.uninstallSkill('copilot'))
+    let uninstallMcpError: unknown
+    try {
+      io.log(dependencies.uninstallCopilotMcp('.'))
+    } catch (error) {
+      uninstallMcpError = error
+    }
+    io.log(dependencies.uninstallSkill('copilot'))
+    if (uninstallMcpError) {
+      throw uninstallMcpError
+    }
   }

   return 0
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/cli/main.ts` around lines 858 - 867, The sequence calling
dependencies.installSkill('copilot') and dependencies.installCopilotMcp('.',
...) (and the reverse for uninstall) can leave the workspace half-configured if
the second call fails; wrap the pair in a try/catch so the second failure
triggers a compensating rollback (call uninstallCopilotMcp/uninstallSkill) for
any prior successful step, or if rollback fails surface a clear error; use the
existing symbols dependencies.installSkill, dependencies.installCopilotMcp,
dependencies.uninstallCopilotMcp, dependencies.uninstallSkill and io.log to
perform the actions and log successes/errors, and ensure
existsSync('graphify-out/graph.json') check remains before attempting install.
🧹 Nitpick comments (2)
tests/unit/stdio-server.test.ts (2)

1209-1213: ⚡ Quick win

Use a unique outside-path filename to avoid cross-test collisions.

Using a fixed outside-secret.ts under a shared parent can become flaky under parallel/sharded runs.

Proposed stability diff
-    const outsidePath = resolve(root, '..', 'outside-secret.ts')
+    const outsidePath = resolve(root, '..', `outside-secret-${process.pid}-${Date.now()}.ts`)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unit/stdio-server.test.ts` around lines 1209 - 1213, The test currently
writes to a fixed file via the outsidePath variable (const outsidePath =
resolve(root, '..', 'outside-secret.ts') and writeFileSync) which can collide
between parallel/sharded runs; change the filename generation to be unique per
test (e.g., append a random/unique suffix or use
crypto.randomUUID()/Date.now()/tmp utilities) when constructing outsidePath so
each test writes to its own distinct file and avoid cross-test collisions while
still using the same resolve/join/writeFileSync flow.

1147-1201: ⚡ Quick win

Strengthen the line_number precedence test with a conflicting location.

This test currently validates snippet extraction with line_number, but it doesn’t prove precedence because the node has no competing source_location/range. Add a conflicting location so the test fails if precedence regresses.

Proposed test-tightening diff
       graph.nodes.push({
         id: 'line-node',
         label: 'LineNode',
         source_file: sourcePath,
+        source_location: 'L1-L1',
         line_number: 2,
         file_type: 'code',
         community: 0,
       })
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unit/stdio-server.test.ts` around lines 1147 - 1201, The test currently
only sets a line_number on the pushed node ('line-node') so it doesn't prove
that line_number takes precedence over a source_location; update the test around
the graph.nodes.push entry for 'line-node' (the same block used by
handleStdioRequest and the context_pack handle 'line-number-handle') to also
supply a conflicting source location/range (or add another node with the same
label and a different source_location/source_file + range), ensuring that the
expected snippet still comes from line_number 2; this will fail if precedence
regresses.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/cli/main.ts`:
- Around line 858-867: The sequence calling dependencies.installSkill('copilot')
and dependencies.installCopilotMcp('.', ...) (and the reverse for uninstall) can
leave the workspace half-configured if the second call fails; wrap the pair in a
try/catch so the second failure triggers a compensating rollback (call
uninstallCopilotMcp/uninstallSkill) for any prior successful step, or if
rollback fails surface a clear error; use the existing symbols
dependencies.installSkill, dependencies.installCopilotMcp,
dependencies.uninstallCopilotMcp, dependencies.uninstallSkill and io.log to
perform the actions and log successes/errors, and ensure
existsSync('graphify-out/graph.json') check remains before attempting install.

---

Nitpick comments:
In `@tests/unit/stdio-server.test.ts`:
- Around line 1209-1213: The test currently writes to a fixed file via the
outsidePath variable (const outsidePath = resolve(root, '..',
'outside-secret.ts') and writeFileSync) which can collide between
parallel/sharded runs; change the filename generation to be unique per test
(e.g., append a random/unique suffix or use crypto.randomUUID()/Date.now()/tmp
utilities) when constructing outsidePath so each test writes to its own distinct
file and avoid cross-test collisions while still using the same
resolve/join/writeFileSync flow.
- Around line 1147-1201: The test currently only sets a line_number on the
pushed node ('line-node') so it doesn't prove that line_number takes precedence
over a source_location; update the test around the graph.nodes.push entry for
'line-node' (the same block used by handleStdioRequest and the context_pack
handle 'line-number-handle') to also supply a conflicting source location/range
(or add another node with the same label and a different
source_location/source_file + range), ensuring that the expected snippet still
comes from line_number 2; this will fail if precedence regresses.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d8afa7cb-c459-4e6d-9268-0044543855df

📥 Commits

Reviewing files that changed from the base of the PR and between b147f66 and 0f64333.

📒 Files selected for processing (8)
  • src/cli/main.ts
  • src/infrastructure/install.ts
  • src/runtime/context-pack.ts
  • src/runtime/stdio/tools.ts
  • tests/unit/cli.test.ts
  • tests/unit/context-pack.test.ts
  • tests/unit/install.test.ts
  • tests/unit/stdio-server.test.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/infrastructure/install.ts
  • tests/unit/cli.test.ts
  • src/runtime/context-pack.ts
  • tests/unit/install.test.ts
  • src/runtime/stdio/tools.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant