@splicr/mcp-server 0.11.0 → 0.11.2
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/dist/cli.js +137 -78
- package/dist/index.js +12 -12
- package/dist/lib/api-client.d.ts +0 -3
- package/dist/lib/api-client.js +0 -4
- package/dist/lib/github-local.d.ts +25 -0
- package/dist/lib/github-local.js +92 -0
- package/dist/tools/get-decisions.js +8 -8
- package/dist/tools/get-project-context.js +2 -2
- package/dist/tools/get-team-status.d.ts +1 -6
- package/dist/tools/get-team-status.js +11 -23
- package/dist/tools/save-from-agent.js +14 -14
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -404,30 +404,50 @@ async function runHook() {
|
|
|
404
404
|
// Gather env signals + fuse with user's prompt
|
|
405
405
|
const { gatherSignals } = await import('./lib/signal-gatherer.js');
|
|
406
406
|
const { fuseSignals } = await import('./lib/signal-fusion.js');
|
|
407
|
-
const { getRelevantContext } = await import('./lib/api-client.js');
|
|
407
|
+
const { getRelevantContext, getProjectContext } = await import('./lib/api-client.js');
|
|
408
|
+
const { detectProject } = await import('./lib/project-detector.js');
|
|
408
409
|
const envSignals = gatherSignals(process.cwd());
|
|
409
410
|
const fused = fuseSignals({ task: userPrompt }, envSignals);
|
|
410
411
|
if (fused.queries.length === 0) {
|
|
411
412
|
process.exit(0);
|
|
412
413
|
return;
|
|
413
414
|
}
|
|
414
|
-
//
|
|
415
|
-
const
|
|
415
|
+
// Check if this is the first message of the session (patterns not yet injected)
|
|
416
|
+
const sessionState = loadSessionState(sessionId);
|
|
417
|
+
const isFirstMessage = !loadSessionMeta(sessionId).patterns_injected;
|
|
418
|
+
// Fetch relevant context + patterns in parallel (patterns only on first message)
|
|
419
|
+
const contextPromise = getRelevantContext({
|
|
416
420
|
queries: fused.queries,
|
|
417
421
|
tech_stack: fused.tech_stack,
|
|
418
|
-
limit: 5,
|
|
422
|
+
limit: 5,
|
|
419
423
|
});
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
424
|
+
let patternsPromise = Promise.resolve({});
|
|
425
|
+
if (isFirstMessage) {
|
|
426
|
+
const detected = await detectProject(process.cwd()).catch(() => null);
|
|
427
|
+
if (detected?.name) {
|
|
428
|
+
patternsPromise = getProjectContext({
|
|
429
|
+
project_name: detected.name,
|
|
430
|
+
project_id: detected.id,
|
|
431
|
+
limit: 1, // we only need patterns, not captures
|
|
432
|
+
}).catch(() => ({}));
|
|
433
|
+
}
|
|
423
434
|
}
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
if (
|
|
435
|
+
const [{ results }, patternData] = await Promise.all([contextPromise, patternsPromise]);
|
|
436
|
+
// Build patterns section (first message only, deterministic enforcement)
|
|
437
|
+
let patternsSection = '';
|
|
438
|
+
if (isFirstMessage && patternData.patterns && patternData.patterns.length > 0) {
|
|
439
|
+
const patternLines = patternData.patterns.map((p, i) => `${i + 1}. **${p.name}** — ${p.description}`).join('\n');
|
|
440
|
+
patternsSection = `ACTIVE TEAM PATTERNS for ${patternData.project_name || 'this project'}:\n${patternLines}\n\nThese patterns are established by your team. Follow them unless explicitly asked to deviate.\n\n---\n\n`;
|
|
441
|
+
// Mark patterns as injected for this session
|
|
442
|
+
saveSessionMeta(sessionId, { patterns_injected: true });
|
|
443
|
+
}
|
|
444
|
+
if ((!results || results.length === 0) && !patternsSection) {
|
|
428
445
|
process.exit(0);
|
|
429
446
|
return;
|
|
430
447
|
}
|
|
448
|
+
// Session-aware dedup: don't inject same captures twice in one session
|
|
449
|
+
const injectedIds = sessionState;
|
|
450
|
+
const freshResults = (results || []).filter((r) => !injectedIds.has(r.id));
|
|
431
451
|
// Take top 3 fresh results
|
|
432
452
|
const topResults = freshResults.slice(0, 3);
|
|
433
453
|
// Track what we injected (IDs + titles for context-aware gate)
|
|
@@ -435,14 +455,22 @@ async function runHook() {
|
|
|
435
455
|
for (const r of topResults)
|
|
436
456
|
injectedIds.add(r.id);
|
|
437
457
|
saveSessionState(sessionId, injectedIds, titles);
|
|
438
|
-
// Format
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
const
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
458
|
+
// Format knowledge context
|
|
459
|
+
let contextSection = '';
|
|
460
|
+
if (topResults.length > 0) {
|
|
461
|
+
const contextLines = topResults.map((r, i) => {
|
|
462
|
+
const why = r.why_relevant || 'Semantically similar to your work context';
|
|
463
|
+
const tags = r.tags?.length ? r.tags.slice(0, 5).join(', ') : '';
|
|
464
|
+
const source = r.source_type || 'unknown';
|
|
465
|
+
return `${i + 1}. "${r.title}" [${source}]${tags ? ` (${tags})` : ''}\n → ${r.insight}\n WHY RELEVANT: ${why}\n DEEP DIVE: get_full_content("${r.id}")`;
|
|
466
|
+
}).join('\n\n');
|
|
467
|
+
contextSection = `SPLICR CONTEXT — The user's saved research matched this task. Use these findings to inform your response:\n\n${contextLines}\n\nACTION: Review above before answering. Call get_full_content(id) for complete articles. Fall back to web search only if these don't cover the question.`;
|
|
468
|
+
}
|
|
469
|
+
const context = patternsSection + contextSection;
|
|
470
|
+
if (!context.trim()) {
|
|
471
|
+
process.exit(0);
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
446
474
|
// Output JSON that Claude Code's hook system understands
|
|
447
475
|
const output = JSON.stringify({
|
|
448
476
|
hookSpecificOutput: {
|
|
@@ -773,6 +801,37 @@ function saveSessionState(sessionId, injectedIds, titles) {
|
|
|
773
801
|
}
|
|
774
802
|
catch { /* non-critical */ }
|
|
775
803
|
}
|
|
804
|
+
function loadSessionMeta(sessionId) {
|
|
805
|
+
if (!sessionId)
|
|
806
|
+
return {};
|
|
807
|
+
try {
|
|
808
|
+
const file = join(SESSION_STATE_DIR, `${sessionId}.json`);
|
|
809
|
+
if (existsSync(file)) {
|
|
810
|
+
const data = JSON.parse(readFileSync(file, 'utf-8'));
|
|
811
|
+
return { patterns_injected: data.patterns_injected || false };
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
catch { /* ignore */ }
|
|
815
|
+
return {};
|
|
816
|
+
}
|
|
817
|
+
function saveSessionMeta(sessionId, meta) {
|
|
818
|
+
if (!sessionId)
|
|
819
|
+
return;
|
|
820
|
+
try {
|
|
821
|
+
if (!existsSync(SESSION_STATE_DIR))
|
|
822
|
+
mkdirSync(SESSION_STATE_DIR, { recursive: true });
|
|
823
|
+
const file = join(SESSION_STATE_DIR, `${sessionId}.json`);
|
|
824
|
+
let existing = {};
|
|
825
|
+
if (existsSync(file)) {
|
|
826
|
+
try {
|
|
827
|
+
existing = JSON.parse(readFileSync(file, 'utf-8'));
|
|
828
|
+
}
|
|
829
|
+
catch { }
|
|
830
|
+
}
|
|
831
|
+
writeFileSync(file, JSON.stringify({ ...existing, ...meta, updated: Date.now() }));
|
|
832
|
+
}
|
|
833
|
+
catch { /* non-critical */ }
|
|
834
|
+
}
|
|
776
835
|
function readStdin() {
|
|
777
836
|
return new Promise((resolve) => {
|
|
778
837
|
let data = '';
|
|
@@ -783,26 +842,26 @@ function readStdin() {
|
|
|
783
842
|
setTimeout(() => resolve(data), 500);
|
|
784
843
|
});
|
|
785
844
|
}
|
|
786
|
-
const SPLICR_INSTRUCTION = `Call get_relevant_context (Splicr MCP) at the START of any task. Pass your understanding of the task (the user's message), any files you're reading/modifying, and any errors you've encountered. It automatically searches the user's knowledge base using full context awareness — your task, git branch, recent commits, changed files, and more.
|
|
787
|
-
|
|
788
|
-
For follow-up or narrow searches during the same task, use search_knowledge with a specific query.
|
|
789
|
-
|
|
790
|
-
When results have high relevance (>50%), call get_full_content(id) to get the complete saved content before responding.
|
|
791
|
-
|
|
792
|
-
Splicr is the user's personal knowledge base. They save articles, tweets, code snippets, and research. Your job is to use it — not ignore it and rely on training data alone.
|
|
793
|
-
|
|
794
|
-
## Auto-save learnings
|
|
795
|
-
At the END of significant sessions (not trivial ones), review what you learned and save decisions/discoveries to Splicr using save_from_agent. Include the context field for attribution.
|
|
796
|
-
|
|
797
|
-
Save ONLY: decisions (chose X over Y because...), discoveries (undocumented behavior), workarounds (non-obvious fixes), synthesis (combined multiple insights into a new approach).
|
|
798
|
-
Do NOT save: file edits, routine commands, anything derivable from git log or reading the current code.
|
|
799
|
-
|
|
800
|
-
Ask yourself: "Would a developer starting a new session benefit from knowing this?" If yes, save it.
|
|
801
|
-
|
|
802
|
-
## Failure modes
|
|
803
|
-
- Splicr returns no results → fall back to web search or training data
|
|
804
|
-
- Splicr returns results but they don't answer the question → supplement with web search
|
|
805
|
-
- Splicr API is unreachable → proceed without it, don't block the session
|
|
845
|
+
const SPLICR_INSTRUCTION = `Call get_relevant_context (Splicr MCP) at the START of any task. Pass your understanding of the task (the user's message), any files you're reading/modifying, and any errors you've encountered. It automatically searches the user's knowledge base using full context awareness — your task, git branch, recent commits, changed files, and more.
|
|
846
|
+
|
|
847
|
+
For follow-up or narrow searches during the same task, use search_knowledge with a specific query.
|
|
848
|
+
|
|
849
|
+
When results have high relevance (>50%), call get_full_content(id) to get the complete saved content before responding.
|
|
850
|
+
|
|
851
|
+
Splicr is the user's personal knowledge base. They save articles, tweets, code snippets, and research. Your job is to use it — not ignore it and rely on training data alone.
|
|
852
|
+
|
|
853
|
+
## Auto-save learnings
|
|
854
|
+
At the END of significant sessions (not trivial ones), review what you learned and save decisions/discoveries to Splicr using save_from_agent. Include the context field for attribution.
|
|
855
|
+
|
|
856
|
+
Save ONLY: decisions (chose X over Y because...), discoveries (undocumented behavior), workarounds (non-obvious fixes), synthesis (combined multiple insights into a new approach).
|
|
857
|
+
Do NOT save: file edits, routine commands, anything derivable from git log or reading the current code.
|
|
858
|
+
|
|
859
|
+
Ask yourself: "Would a developer starting a new session benefit from knowing this?" If yes, save it.
|
|
860
|
+
|
|
861
|
+
## Failure modes
|
|
862
|
+
- Splicr returns no results → fall back to web search or training data
|
|
863
|
+
- Splicr returns results but they don't answer the question → supplement with web search
|
|
864
|
+
- Splicr API is unreachable → proceed without it, don't block the session
|
|
806
865
|
- You forgot to save learnings → the Stop hook will remind you`;
|
|
807
866
|
const SPLICR_MARKER = '# Splicr';
|
|
808
867
|
function getInstructionFiles() {
|
|
@@ -1052,17 +1111,17 @@ async function teamCommand() {
|
|
|
1052
1111
|
break;
|
|
1053
1112
|
}
|
|
1054
1113
|
default:
|
|
1055
|
-
console.error(`
|
|
1056
|
-
Splicr Teams
|
|
1057
|
-
|
|
1058
|
-
Commands:
|
|
1059
|
-
team create "Name" Create a new team
|
|
1060
|
-
team list List your teams
|
|
1061
|
-
team invite Show invite link for your team
|
|
1062
|
-
team join <code> Join a team by invite code
|
|
1063
|
-
|
|
1064
|
-
Or use setup --join:
|
|
1065
|
-
setup --join <code> Sign up + join team + configure agents (one command)
|
|
1114
|
+
console.error(`
|
|
1115
|
+
Splicr Teams
|
|
1116
|
+
|
|
1117
|
+
Commands:
|
|
1118
|
+
team create "Name" Create a new team
|
|
1119
|
+
team list List your teams
|
|
1120
|
+
team invite Show invite link for your team
|
|
1121
|
+
team join <code> Join a team by invite code
|
|
1122
|
+
|
|
1123
|
+
Or use setup --join:
|
|
1124
|
+
setup --join <code> Sign up + join team + configure agents (one command)
|
|
1066
1125
|
`);
|
|
1067
1126
|
break;
|
|
1068
1127
|
}
|
|
@@ -1079,34 +1138,34 @@ function printManualConfig() {
|
|
|
1079
1138
|
console.error(' }\n');
|
|
1080
1139
|
}
|
|
1081
1140
|
function printHelp() {
|
|
1082
|
-
console.error(`
|
|
1083
|
-
Splicr — route what you read to what you're building
|
|
1084
|
-
|
|
1085
|
-
Getting started:
|
|
1086
|
-
npx @splicr/mcp-server setup Sign up + configure all agents (one command)
|
|
1087
|
-
npx @splicr/mcp-server setup --join <code> Sign up + join team + configure agents
|
|
1088
|
-
|
|
1089
|
-
Commands:
|
|
1090
|
-
setup One-time setup: authenticate + configure agents + hooks
|
|
1091
|
-
login Re-authenticate
|
|
1092
|
-
team create "Name" Create a new team
|
|
1093
|
-
team list List your teams
|
|
1094
|
-
team invite Show invite link for your team
|
|
1095
|
-
team join <code> Join a team by invite code
|
|
1096
|
-
dashboard Open knowledge dashboard in browser
|
|
1097
|
-
uninstall Remove Splicr from all coding agents
|
|
1098
|
-
|
|
1099
|
-
Supported agents (auto-detected):
|
|
1100
|
-
Claude Code, Codex, Cursor, Cline, Antigravity
|
|
1101
|
-
|
|
1102
|
-
Quick start:
|
|
1103
|
-
1. Run: npx @splicr/mcp-server setup
|
|
1104
|
-
2. Save knowledge from anywhere:
|
|
1105
|
-
- Telegram: t.me/SplicrBot (send links from your phone)
|
|
1106
|
-
- Extension: chromewebstore.google.com/detail/dllhofjfmkmbdadilbdgojapfbmlhnla
|
|
1107
|
-
- Agent: save_from_agent tool (agents save learnings)
|
|
1108
|
-
3. Open any coding agent — your saves show up when relevant
|
|
1109
|
-
4. Dashboard: splicr.dev/dashboard
|
|
1141
|
+
console.error(`
|
|
1142
|
+
Splicr — route what you read to what you're building
|
|
1143
|
+
|
|
1144
|
+
Getting started:
|
|
1145
|
+
npx @splicr/mcp-server setup Sign up + configure all agents (one command)
|
|
1146
|
+
npx @splicr/mcp-server setup --join <code> Sign up + join team + configure agents
|
|
1147
|
+
|
|
1148
|
+
Commands:
|
|
1149
|
+
setup One-time setup: authenticate + configure agents + hooks
|
|
1150
|
+
login Re-authenticate
|
|
1151
|
+
team create "Name" Create a new team
|
|
1152
|
+
team list List your teams
|
|
1153
|
+
team invite Show invite link for your team
|
|
1154
|
+
team join <code> Join a team by invite code
|
|
1155
|
+
dashboard Open knowledge dashboard in browser
|
|
1156
|
+
uninstall Remove Splicr from all coding agents
|
|
1157
|
+
|
|
1158
|
+
Supported agents (auto-detected):
|
|
1159
|
+
Claude Code, Codex, Cursor, Cline, Antigravity
|
|
1160
|
+
|
|
1161
|
+
Quick start:
|
|
1162
|
+
1. Run: npx @splicr/mcp-server setup
|
|
1163
|
+
2. Save knowledge from anywhere:
|
|
1164
|
+
- Telegram: t.me/SplicrBot (send links from your phone)
|
|
1165
|
+
- Extension: chromewebstore.google.com/detail/dllhofjfmkmbdadilbdgojapfbmlhnla
|
|
1166
|
+
- Agent: save_from_agent tool (agents save learnings)
|
|
1167
|
+
3. Open any coding agent — your saves show up when relevant
|
|
1168
|
+
4. Dashboard: splicr.dev/dashboard
|
|
1110
1169
|
`);
|
|
1111
1170
|
}
|
|
1112
1171
|
main().catch(err => {
|
package/dist/index.js
CHANGED
|
@@ -35,18 +35,18 @@ for (const signal of ['SIGTERM', 'SIGINT']) {
|
|
|
35
35
|
}
|
|
36
36
|
const server = new Server({ name: 'splicr', version: '0.1.0' }, {
|
|
37
37
|
capabilities: { tools: {} },
|
|
38
|
-
instructions: `Splicr is the user's personal knowledge base — articles, tweets, code snippets, GitHub repos, and agent learnings saved from across the web. It is your PRIMARY source of context. Using it is not optional.
|
|
39
|
-
|
|
40
|
-
CONTRACTS (you must follow these):
|
|
41
|
-
1. BEFORE answering any question, BEFORE web searching, BEFORE relying on training data — call get_relevant_context with the user's task. This is not a suggestion. The user saved knowledge specifically for moments like this.
|
|
42
|
-
2. When results have relevance >50%, call get_full_content(id) to get the complete saved content. Use it to ground your response.
|
|
43
|
-
2b. When results include a compiled_page field, prefer get_compiled_page(id) — it's a synthesized wiki page merging multiple related sources into one structured document. More useful than reading individual captures.
|
|
44
|
-
3. Only fall back to web search or training data if Splicr returns no results or results don't cover the question.
|
|
45
|
-
4. At the END of significant sessions, call save_from_agent with decisions, discoveries, or workarounds you made. Include the context field for attribution.
|
|
46
|
-
|
|
47
|
-
FAILURE MODES:
|
|
48
|
-
- Splicr returns no results → proceed with web search or training data
|
|
49
|
-
- Splicr API is unreachable → proceed without it, don't block the session
|
|
38
|
+
instructions: `Splicr is the user's personal knowledge base — articles, tweets, code snippets, GitHub repos, and agent learnings saved from across the web. It is your PRIMARY source of context. Using it is not optional.
|
|
39
|
+
|
|
40
|
+
CONTRACTS (you must follow these):
|
|
41
|
+
1. BEFORE answering any question, BEFORE web searching, BEFORE relying on training data — call get_relevant_context with the user's task. This is not a suggestion. The user saved knowledge specifically for moments like this.
|
|
42
|
+
2. When results have relevance >50%, call get_full_content(id) to get the complete saved content. Use it to ground your response.
|
|
43
|
+
2b. When results include a compiled_page field, prefer get_compiled_page(id) — it's a synthesized wiki page merging multiple related sources into one structured document. More useful than reading individual captures.
|
|
44
|
+
3. Only fall back to web search or training data if Splicr returns no results or results don't cover the question.
|
|
45
|
+
4. At the END of significant sessions, call save_from_agent with decisions, discoveries, or workarounds you made. Include the context field for attribution.
|
|
46
|
+
|
|
47
|
+
FAILURE MODES:
|
|
48
|
+
- Splicr returns no results → proceed with web search or training data
|
|
49
|
+
- Splicr API is unreachable → proceed without it, don't block the session
|
|
50
50
|
- Results don't match the question → supplement with other sources`,
|
|
51
51
|
});
|
|
52
52
|
// List tools
|
package/dist/lib/api-client.d.ts
CHANGED
package/dist/lib/api-client.js
CHANGED
|
@@ -104,8 +104,4 @@ export async function getDecisions(params) {
|
|
|
104
104
|
const data = await apiRequest('POST', '/mcp/decisions', params);
|
|
105
105
|
return { results: data.results ?? [] };
|
|
106
106
|
}
|
|
107
|
-
export async function getTeamStatus(params) {
|
|
108
|
-
const query = params.project ? `?project=${encodeURIComponent(params.project)}` : '';
|
|
109
|
-
return await apiRequest('GET', `/mcp/team-status${query}`);
|
|
110
|
-
}
|
|
111
107
|
export { API_URL };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local GitHub Status - uses `gh` CLI instead of stored token.
|
|
3
|
+
* Zero onboarding friction: if the user has `gh auth login` done, this just works.
|
|
4
|
+
*/
|
|
5
|
+
export interface TeamStatus {
|
|
6
|
+
repo: string;
|
|
7
|
+
open_prs: Array<{
|
|
8
|
+
number: number;
|
|
9
|
+
title: string;
|
|
10
|
+
author: string;
|
|
11
|
+
branch: string;
|
|
12
|
+
updated: string;
|
|
13
|
+
draft: boolean;
|
|
14
|
+
}>;
|
|
15
|
+
recent_merges: Array<{
|
|
16
|
+
title: string;
|
|
17
|
+
author: string;
|
|
18
|
+
merged_at: string;
|
|
19
|
+
}>;
|
|
20
|
+
active_branches: Array<{
|
|
21
|
+
name: string;
|
|
22
|
+
}>;
|
|
23
|
+
error?: string;
|
|
24
|
+
}
|
|
25
|
+
export declare function getLocalGitHubStatus(cwd: string): Promise<TeamStatus>;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local GitHub Status - uses `gh` CLI instead of stored token.
|
|
3
|
+
* Zero onboarding friction: if the user has `gh auth login` done, this just works.
|
|
4
|
+
*/
|
|
5
|
+
import { execSync } from 'child_process';
|
|
6
|
+
import { getGitRemoteUrl, normalizeGitUrl } from './project-detector.js';
|
|
7
|
+
/** Check if `gh` CLI is available and authenticated */
|
|
8
|
+
function isGhAvailable() {
|
|
9
|
+
try {
|
|
10
|
+
execSync('gh auth status', { encoding: 'utf-8', stdio: 'pipe' });
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/** Extract owner/repo from a git remote URL */
|
|
18
|
+
function extractRepoFullName(cwd) {
|
|
19
|
+
const remote = getGitRemoteUrl(cwd);
|
|
20
|
+
if (!remote)
|
|
21
|
+
return null;
|
|
22
|
+
const normalized = normalizeGitUrl(remote);
|
|
23
|
+
// https://github.com/owner/repo -> owner/repo
|
|
24
|
+
const match = normalized.match(/github\.com\/([^/]+\/[^/]+)/);
|
|
25
|
+
return match ? match[1] : null;
|
|
26
|
+
}
|
|
27
|
+
/** Run a gh api command and parse JSON result */
|
|
28
|
+
function ghApi(endpoint) {
|
|
29
|
+
try {
|
|
30
|
+
const result = execSync(`gh api "${endpoint}" --paginate`, {
|
|
31
|
+
encoding: 'utf-8',
|
|
32
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
33
|
+
timeout: 10000,
|
|
34
|
+
});
|
|
35
|
+
return JSON.parse(result);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export async function getLocalGitHubStatus(cwd) {
|
|
42
|
+
const repoFullName = extractRepoFullName(cwd);
|
|
43
|
+
if (!repoFullName) {
|
|
44
|
+
return { repo: 'unknown', open_prs: [], recent_merges: [], active_branches: [], error: 'No GitHub remote found in this directory.' };
|
|
45
|
+
}
|
|
46
|
+
if (!isGhAvailable()) {
|
|
47
|
+
return { repo: repoFullName, open_prs: [], recent_merges: [], active_branches: [], error: 'GitHub CLI (gh) not authenticated. Run `gh auth login` to enable team status.' };
|
|
48
|
+
}
|
|
49
|
+
const status = {
|
|
50
|
+
repo: repoFullName,
|
|
51
|
+
open_prs: [],
|
|
52
|
+
recent_merges: [],
|
|
53
|
+
active_branches: [],
|
|
54
|
+
};
|
|
55
|
+
// Fetch open PRs
|
|
56
|
+
const openPRs = ghApi(`repos/${repoFullName}/pulls?state=open&sort=updated&direction=desc&per_page=10`);
|
|
57
|
+
if (openPRs) {
|
|
58
|
+
status.open_prs = openPRs.map(pr => ({
|
|
59
|
+
number: pr.number,
|
|
60
|
+
title: pr.title,
|
|
61
|
+
author: pr.user.login,
|
|
62
|
+
branch: pr.head?.ref || pr.headRefName || '',
|
|
63
|
+
updated: pr.updated_at || pr.updatedAt || '',
|
|
64
|
+
draft: pr.draft ?? pr.isDraft ?? false,
|
|
65
|
+
}));
|
|
66
|
+
}
|
|
67
|
+
// Fetch recently merged PRs
|
|
68
|
+
const mergedPRs = ghApi(`repos/${repoFullName}/pulls?state=closed&sort=updated&direction=desc&per_page=10`);
|
|
69
|
+
if (mergedPRs) {
|
|
70
|
+
const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
|
|
71
|
+
status.recent_merges = mergedPRs
|
|
72
|
+
.filter(pr => {
|
|
73
|
+
const mergedDate = pr.merged_at || pr.mergedAt || pr.updated_at || pr.updatedAt;
|
|
74
|
+
return mergedDate && new Date(mergedDate) >= sevenDaysAgo;
|
|
75
|
+
})
|
|
76
|
+
.map(pr => ({
|
|
77
|
+
title: pr.title,
|
|
78
|
+
author: pr.user.login,
|
|
79
|
+
merged_at: pr.merged_at || pr.mergedAt || pr.updated_at || pr.updatedAt || '',
|
|
80
|
+
}));
|
|
81
|
+
}
|
|
82
|
+
// Fetch branches
|
|
83
|
+
const branches = ghApi(`repos/${repoFullName}/branches?per_page=20`);
|
|
84
|
+
if (branches) {
|
|
85
|
+
const skipBranches = new Set(['main', 'master', 'develop', 'dev', 'staging', 'production']);
|
|
86
|
+
status.active_branches = branches
|
|
87
|
+
.filter(b => !skipBranches.has(b.name))
|
|
88
|
+
.slice(0, 10)
|
|
89
|
+
.map(b => ({ name: b.name }));
|
|
90
|
+
}
|
|
91
|
+
return status;
|
|
92
|
+
}
|
|
@@ -3,14 +3,14 @@ import { detectProject } from '../lib/project-detector.js';
|
|
|
3
3
|
import * as session from '../lib/session-state.js';
|
|
4
4
|
export const getDecisionsSchema = {
|
|
5
5
|
name: 'get_decisions',
|
|
6
|
-
description: `Query the team's decision log - technical decisions, patterns, discoveries, and failures recorded during agent sessions.
|
|
7
|
-
|
|
8
|
-
Use when:
|
|
9
|
-
- Before making an architecture or technology choice (see what the team decided before)
|
|
10
|
-
- When someone asks "why did we do X this way?"
|
|
11
|
-
- When evaluating alternatives for a new approach
|
|
12
|
-
- To check if something was tried before and failed
|
|
13
|
-
|
|
6
|
+
description: `Query the team's decision log - technical decisions, patterns, discoveries, and failures recorded during agent sessions.
|
|
7
|
+
|
|
8
|
+
Use when:
|
|
9
|
+
- Before making an architecture or technology choice (see what the team decided before)
|
|
10
|
+
- When someone asks "why did we do X this way?"
|
|
11
|
+
- When evaluating alternatives for a new approach
|
|
12
|
+
- To check if something was tried before and failed
|
|
13
|
+
|
|
14
14
|
Returns structured decisions with rationale, alternatives considered, and anti-pattern warnings.`,
|
|
15
15
|
inputSchema: {
|
|
16
16
|
type: 'object',
|
|
@@ -4,8 +4,8 @@ import { gatherSignals } from '../lib/signal-gatherer.js';
|
|
|
4
4
|
import { fuseSignals } from '../lib/signal-fusion.js';
|
|
5
5
|
export const getProjectContextSchema = {
|
|
6
6
|
name: 'get_project_context',
|
|
7
|
-
description: `Show what's new in a project's knowledge feed from Splicr. Only call this when the user explicitly asks to see new saves, recent context, or "what's new." Do NOT call this proactively at session start.
|
|
8
|
-
|
|
7
|
+
description: `Show what's new in a project's knowledge feed from Splicr. Only call this when the user explicitly asks to see new saves, recent context, or "what's new." Do NOT call this proactively at session start.
|
|
8
|
+
|
|
9
9
|
Uses a watermark system — only returns captures saved since the last time context was served for this project. Safe to call repeatedly; it will return empty if nothing is new.`,
|
|
10
10
|
inputSchema: {
|
|
11
11
|
type: 'object',
|
|
@@ -3,12 +3,7 @@ export declare const getTeamStatusSchema: {
|
|
|
3
3
|
description: string;
|
|
4
4
|
inputSchema: {
|
|
5
5
|
type: "object";
|
|
6
|
-
properties: {
|
|
7
|
-
project: {
|
|
8
|
-
type: "string";
|
|
9
|
-
description: string;
|
|
10
|
-
};
|
|
11
|
-
};
|
|
6
|
+
properties: {};
|
|
12
7
|
};
|
|
13
8
|
};
|
|
14
9
|
export declare function handleGetTeamStatus(args: Record<string, unknown>): Promise<string>;
|
|
@@ -1,34 +1,22 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { detectProject } from '../lib/project-detector.js';
|
|
1
|
+
import { getLocalGitHubStatus } from '../lib/github-local.js';
|
|
3
2
|
import * as session from '../lib/session-state.js';
|
|
4
3
|
export const getTeamStatusSchema = {
|
|
5
4
|
name: 'get_team_status',
|
|
6
|
-
description: `Get a brief on what the team is working on right now. Shows open PRs, active branches, and recent merges from GitHub.
|
|
7
|
-
|
|
8
|
-
Use when:
|
|
9
|
-
- Starting a session and want to know what's in flight
|
|
10
|
-
- Before starting work on a feature (check if someone else is already on it)
|
|
11
|
-
- To understand recent changes to the codebase
|
|
12
|
-
|
|
13
|
-
Requires GitHub
|
|
5
|
+
description: `Get a brief on what the team is working on right now. Shows open PRs, active branches, and recent merges from GitHub.
|
|
6
|
+
|
|
7
|
+
Use when:
|
|
8
|
+
- Starting a session and want to know what's in flight
|
|
9
|
+
- Before starting work on a feature (check if someone else is already on it)
|
|
10
|
+
- To understand recent changes to the codebase
|
|
11
|
+
|
|
12
|
+
Requires GitHub CLI (gh) to be authenticated. Run \`gh auth login\` if not set up.`,
|
|
14
13
|
inputSchema: {
|
|
15
14
|
type: 'object',
|
|
16
|
-
properties: {
|
|
17
|
-
project: { type: 'string', description: 'Project name or "auto" (default: auto)' },
|
|
18
|
-
},
|
|
15
|
+
properties: {},
|
|
19
16
|
},
|
|
20
17
|
};
|
|
21
18
|
export async function handleGetTeamStatus(args) {
|
|
22
|
-
const
|
|
23
|
-
let projectName;
|
|
24
|
-
if (projectArg === 'auto') {
|
|
25
|
-
const detected = await detectProject(process.cwd());
|
|
26
|
-
projectName = detected?.name;
|
|
27
|
-
}
|
|
28
|
-
else {
|
|
29
|
-
projectName = projectArg;
|
|
30
|
-
}
|
|
31
|
-
const data = await getTeamStatus({ project: projectName });
|
|
19
|
+
const data = await getLocalGitHubStatus(process.cwd());
|
|
32
20
|
session.recordToolCall();
|
|
33
21
|
if (data.error) {
|
|
34
22
|
return data.error;
|
|
@@ -3,20 +3,20 @@ import { detectProject } from '../lib/project-detector.js';
|
|
|
3
3
|
import * as session from '../lib/session-state.js';
|
|
4
4
|
export const saveFromAgentSchema = {
|
|
5
5
|
name: 'save_from_agent',
|
|
6
|
-
description: `Save knowledge to Splicr. Call this in two scenarios:
|
|
7
|
-
|
|
8
|
-
1. When the user explicitly asks to save/remember something
|
|
9
|
-
2. At the end of significant sessions — save decisions, discoveries, and workarounds (NOT routine actions)
|
|
10
|
-
|
|
11
|
-
For auto-save at session end, include the "context" field with attribution metadata.
|
|
12
|
-
|
|
13
|
-
**Memory types** (use memory_type to categorize):
|
|
14
|
-
- "fact" — permanent knowledge (API uses X pattern, team decided Y). Default if omitted.
|
|
15
|
-
- "procedural" — reusable how-to (deploy steps, workarounds, setup instructions)
|
|
16
|
-
- "episodic" — session-level context (what happened this session, investigation results)
|
|
17
|
-
- "scratchpad" — short-lived working memory (temp notes, in-progress thinking)
|
|
18
|
-
|
|
19
|
-
What to save: decisions (chose X over Y because...), discoveries (found undocumented behavior), workarounds (non-obvious fix), synthesis (combined multiple insights).
|
|
6
|
+
description: `Save knowledge to Splicr. Call this in two scenarios:
|
|
7
|
+
|
|
8
|
+
1. When the user explicitly asks to save/remember something
|
|
9
|
+
2. At the end of significant sessions — save decisions, discoveries, and workarounds (NOT routine actions)
|
|
10
|
+
|
|
11
|
+
For auto-save at session end, include the "context" field with attribution metadata.
|
|
12
|
+
|
|
13
|
+
**Memory types** (use memory_type to categorize):
|
|
14
|
+
- "fact" — permanent knowledge (API uses X pattern, team decided Y). Default if omitted.
|
|
15
|
+
- "procedural" — reusable how-to (deploy steps, workarounds, setup instructions)
|
|
16
|
+
- "episodic" — session-level context (what happened this session, investigation results)
|
|
17
|
+
- "scratchpad" — short-lived working memory (temp notes, in-progress thinking)
|
|
18
|
+
|
|
19
|
+
What to save: decisions (chose X over Y because...), discoveries (found undocumented behavior), workarounds (non-obvious fix), synthesis (combined multiple insights).
|
|
20
20
|
What NOT to save: file edits (git has those), routine commands, anything derivable from reading the code.`,
|
|
21
21
|
inputSchema: {
|
|
22
22
|
type: 'object',
|