@phuetz/code-buddy 1.3.1 → 1.3.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/README.md +2 -2
- package/dist/agent/autonomous/agentic-coding-runner.d.ts +2 -2
- package/dist/agent/autonomous/agentic-coding-runner.js +24 -24
- package/dist/agent/autonomous/checkpoint-manager.d.ts +2 -2
- package/dist/agent/base-agent.d.ts +1 -1
- package/dist/agent/base-agent.js +1 -1
- package/dist/agent/hermes-tool-parity-local.js +2 -2
- package/dist/agent/tool-handler.js +2 -2
- package/dist/codebuddy/providers/provider-chatgpt-responses.d.ts +4 -0
- package/dist/codebuddy/providers/provider-chatgpt-responses.js +34 -13
- package/dist/codebuddy/providers/provider-openai-compat.js +8 -0
- package/dist/codebuddy/tool-definitions/code-explorer-tools.d.ts +8 -0
- package/dist/codebuddy/tool-definitions/code-explorer-tools.js +24 -0
- package/dist/codebuddy/tool-definitions/graph-tools.d.ts +1 -1
- package/dist/codebuddy/tool-definitions/graph-tools.js +1 -1
- package/dist/codebuddy/tool-definitions/index.d.ts +1 -1
- package/dist/codebuddy/tool-definitions/index.js +2 -2
- package/dist/codebuddy/tools.d.ts +2 -2
- package/dist/codebuddy/tools.js +13 -13
- package/dist/collaboration/ai-colab-manager.js +9 -4
- package/dist/commands/cli/code-explorer-commands.d.ts +7 -0
- package/dist/commands/cli/code-explorer-commands.js +40 -0
- package/dist/commands/cli/native-engine-commands.js +64 -0
- package/dist/commands/handlers/graph-handlers.d.ts +1 -1
- package/dist/commands/handlers/graph-handlers.js +1 -1
- package/dist/config/model-tools.js +1 -1
- package/dist/daemon/autonomous-loop.d.ts +0 -7
- package/dist/daemon/autonomous-loop.js +24 -14
- package/dist/fleet/capability-registry.js +9 -1
- package/dist/fleet/colab-store.d.ts +109 -3
- package/dist/fleet/colab-store.js +181 -6
- package/dist/index.js +28 -11
- package/dist/kanban/colab-kanban-adapter.d.ts +31 -0
- package/dist/kanban/colab-kanban-adapter.js +232 -0
- package/dist/knowledge/community-detector.d.ts +1 -1
- package/dist/knowledge/community-detector.js +1 -1
- package/dist/knowledge/process-detector.d.ts +1 -1
- package/dist/knowledge/process-detector.js +1 -1
- package/dist/plugins/{gitnexus/GitNexusMCPClient.d.ts → code-explorer/CodeExplorerMCPClient.d.ts} +9 -9
- package/dist/plugins/{gitnexus/GitNexusMCPClient.js → code-explorer/CodeExplorerMCPClient.js} +17 -17
- package/dist/plugins/{gitnexus/GitNexusManager.d.ts → code-explorer/CodeExplorerManager.d.ts} +16 -16
- package/dist/plugins/{gitnexus/GitNexusManager.js → code-explorer/CodeExplorerManager.js} +35 -35
- package/dist/plugins/code-explorer/index.d.ts +9 -0
- package/dist/plugins/code-explorer/index.js +8 -0
- package/dist/providers/provider-catalog.js +23 -1
- package/dist/services/prompt-builder.js +9 -9
- package/dist/skills/parser.js +1 -1
- package/dist/tools/{gitnexus-tool.d.ts → code-explorer-tool.d.ts} +9 -9
- package/dist/tools/{gitnexus-tool.js → code-explorer-tool.js} +16 -16
- package/dist/tools/metadata.js +3 -3
- package/dist/tools/registry/code-explorer-tools.d.ts +18 -0
- package/dist/tools/registry/{gitnexus-tools.js → code-explorer-tools.js} +14 -14
- package/dist/tools/registry/graph-tools.d.ts +1 -1
- package/dist/tools/registry/graph-tools.js +1 -1
- package/dist/tools/registry/index.d.ts +1 -1
- package/dist/tools/registry/index.js +6 -6
- package/dist/tools/registry/kanban-tools.d.ts +1 -1
- package/dist/tools/registry/kanban-tools.js +2 -2
- package/package.json +1 -1
- package/dist/codebuddy/tool-definitions/gitnexus-tools.d.ts +0 -8
- package/dist/codebuddy/tool-definitions/gitnexus-tools.js +0 -24
- package/dist/commands/cli/gitnexus-commands.d.ts +0 -7
- package/dist/commands/cli/gitnexus-commands.js +0 -42
- package/dist/plugins/gitnexus/index.d.ts +0 -9
- package/dist/plugins/gitnexus/index.js +0 -8
- package/dist/tools/registry/gitnexus-tools.d.ts +0 -18
package/dist/index.js
CHANGED
|
@@ -508,6 +508,23 @@ async function saveCommandLineSettings(apiKey, baseURL) {
|
|
|
508
508
|
cli.warn(`⚠️ Could not save settings to file: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
509
509
|
}
|
|
510
510
|
}
|
|
511
|
+
/**
|
|
512
|
+
* A saved/default model is only usable if it matches the detected provider's
|
|
513
|
+
* backend: a `grok-*` slug 404s on the ChatGPT/Codex backend, and a `gpt-5.*`
|
|
514
|
+
* slug 404s on xAI. We only enforce this for the two providers with a known
|
|
515
|
+
* hard incompatibility (grok ↔ chatgpt); other providers are left untouched so
|
|
516
|
+
* local/OpenAI/Anthropic model names pass through as before.
|
|
517
|
+
*/
|
|
518
|
+
function isModelCompatibleWithProvider(model, provider) {
|
|
519
|
+
if (!provider)
|
|
520
|
+
return true;
|
|
521
|
+
const looksGrok = /grok/i.test(model);
|
|
522
|
+
if (provider === 'grok')
|
|
523
|
+
return looksGrok;
|
|
524
|
+
if (provider === 'chatgpt')
|
|
525
|
+
return /^(gpt-|o[1-9]|codex)/i.test(model) && !looksGrok;
|
|
526
|
+
return true;
|
|
527
|
+
}
|
|
511
528
|
// Load model from detected provider or user settings
|
|
512
529
|
async function loadModel() {
|
|
513
530
|
await ensureEnvLoaded();
|
|
@@ -517,15 +534,15 @@ async function loadModel() {
|
|
|
517
534
|
const detected = await getDetectedProvider();
|
|
518
535
|
// 2. Project/user settings override auto-detection — UNLESS the saved model is
|
|
519
536
|
// incompatible with the detected provider. A `gpt-5.5` left in settings.json
|
|
520
|
-
// would 404 against xAI after `buddy login xai`;
|
|
537
|
+
// would 404 against xAI after `buddy login xai`; symmetrically, a stale
|
|
538
|
+
// `grok-code-fast-1` default 404s against the ChatGPT/Codex backend (which
|
|
539
|
+
// then falls back to another unsupported slug and crashes). In either
|
|
540
|
+
// mismatch, prefer the detected provider's own default model.
|
|
521
541
|
try {
|
|
522
542
|
const getSettingsManager = await lazyImport.settingsManager();
|
|
523
543
|
const settingsModel = getSettingsManager().getCurrentModel();
|
|
524
|
-
if (settingsModel) {
|
|
525
|
-
|
|
526
|
-
if (!grokProvider || /grok/i.test(settingsModel)) {
|
|
527
|
-
return settingsModel;
|
|
528
|
-
}
|
|
544
|
+
if (settingsModel && isModelCompatibleWithProvider(settingsModel, detected?.provider)) {
|
|
545
|
+
return settingsModel;
|
|
529
546
|
}
|
|
530
547
|
}
|
|
531
548
|
catch (_err) {
|
|
@@ -827,7 +844,7 @@ async function processPromptHeadless(prompt, apiKey, baseURL, model, maxToolRoun
|
|
|
827
844
|
const previousHeadless = process.env.CODEBUDDY_HEADLESS;
|
|
828
845
|
// Headless defaults MCP OFF (startup cost / determinism), but respect an
|
|
829
846
|
// explicit opt-in so `CODEBUDDY_DISABLE_MCP=false buddy -p …` can use MCP
|
|
830
|
-
// servers (e.g. the Code Explorer /
|
|
847
|
+
// servers (e.g. the Code Explorer / code-explorer bridge, or the benchmark's
|
|
831
848
|
// "with graph" condition). Mirrors the opt-in pattern in goal-cli.ts.
|
|
832
849
|
process.env.CODEBUDDY_DISABLE_MCP = process.env.CODEBUDDY_DISABLE_MCP ?? 'true';
|
|
833
850
|
process.env.CODEBUDDY_HEADLESS = 'true';
|
|
@@ -839,7 +856,7 @@ async function processPromptHeadless(prompt, apiKey, baseURL, model, maxToolRoun
|
|
|
839
856
|
await applyActiveLlmFailover(agent);
|
|
840
857
|
await agent.systemPromptReady;
|
|
841
858
|
// When MCP is opted in for this headless run, wait for the servers to finish
|
|
842
|
-
// connecting so their tools (e.g. the Code Explorer /
|
|
859
|
+
// connecting so their tools (e.g. the Code Explorer / code-explorer bridge) are
|
|
843
860
|
// registered before the first turn — otherwise the one-shot turn races init
|
|
844
861
|
// and the agent never sees the MCP tools.
|
|
845
862
|
if (process.env.CODEBUDDY_DISABLE_MCP !== 'true') {
|
|
@@ -2461,9 +2478,9 @@ addLazyCommandGroup(program, 'fleet', 'Inspect Fleet routing and dispatch policy
|
|
|
2461
2478
|
const { registerFleetCommands } = await import('./commands/cli/fleet-commands.js');
|
|
2462
2479
|
registerFleetCommands(program);
|
|
2463
2480
|
});
|
|
2464
|
-
addLazyCommandGroup(program, '
|
|
2465
|
-
const {
|
|
2466
|
-
|
|
2481
|
+
addLazyCommandGroup(program, 'code-explorer', 'Interact with CodeExplorer for code understanding and session syncing', async () => {
|
|
2482
|
+
const { registerCodeExplorerCommands } = await import('./commands/cli/code-explorer-commands.js');
|
|
2483
|
+
registerCodeExplorerCommands(program);
|
|
2467
2484
|
});
|
|
2468
2485
|
addLazyCommandGroup(program, 'hermes', 'Inspect the native Hermes-inspired Code Buddy agent profile', async () => {
|
|
2469
2486
|
const { registerHermesCommands } = await import('./commands/cli/hermes-commands.js');
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { FleetColabStore } from '../fleet/colab-store.js';
|
|
2
|
+
import type { CreateKanbanCardInput, KanbanCard, KanbanStoreOptions, ListKanbanCardsFilter } from './kanban-store.js';
|
|
3
|
+
export declare class ColabKanbanAdapter {
|
|
4
|
+
private readonly store;
|
|
5
|
+
private readonly createId;
|
|
6
|
+
constructor(options?: KanbanStoreOptions);
|
|
7
|
+
/** The shared board file — now colab-tasks.json, not kanban-board.json. */
|
|
8
|
+
get path(): string;
|
|
9
|
+
createCard(input: CreateKanbanCardInput): Promise<KanbanCard>;
|
|
10
|
+
listCards(filter?: ListKanbanCardsFilter): Promise<KanbanCard[]>;
|
|
11
|
+
showCard(id: string): Promise<KanbanCard>;
|
|
12
|
+
completeCard(id: string, comment?: string, author?: string): Promise<KanbanCard>;
|
|
13
|
+
blockCard(id: string, reason: string, author?: string): Promise<KanbanCard>;
|
|
14
|
+
unblockCard(id: string, comment?: string, author?: string): Promise<KanbanCard>;
|
|
15
|
+
commentCard(id: string, text: string, author?: string): Promise<KanbanCard>;
|
|
16
|
+
heartbeatCard(id: string, message?: string, author?: string): Promise<KanbanCard>;
|
|
17
|
+
linkCard(id: string, target: string, label?: string): Promise<KanbanCard>;
|
|
18
|
+
private getOrThrow;
|
|
19
|
+
private buildCardId;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Migrate cards from a legacy `kanban-board.json` (read as {@link KanbanCard}s)
|
|
23
|
+
* into the unified fleet board. Idempotent: a card whose id already exists is
|
|
24
|
+
* skipped, as are archived cards (colab has no archived state). Comments and
|
|
25
|
+
* free-form links are carried over; ephemeral heartbeats are not. Returns the
|
|
26
|
+
* imported / skipped ids so a CLI can report the migration.
|
|
27
|
+
*/
|
|
28
|
+
export declare function importKanbanCards(cards: KanbanCard[], store: FleetColabStore): {
|
|
29
|
+
imported: string[];
|
|
30
|
+
skipped: string[];
|
|
31
|
+
};
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ColabKanbanAdapter — the unification seam (Hermes/OpenClaw parity).
|
|
3
|
+
*
|
|
4
|
+
* Exposes the subset of the {@link KanbanStore} API that the `kanban_*` tools
|
|
5
|
+
* use, but backed by {@link FleetColabStore} so the agent's kanban tools and the
|
|
6
|
+
* autonomous daemon drive ONE shared board (`.codebuddy/colab-tasks.json`)
|
|
7
|
+
* instead of two disconnected files. A card created with `kanban_create` is now
|
|
8
|
+
* claimable by the daemon; a task the daemon completes shows up in `kanban_list`.
|
|
9
|
+
*
|
|
10
|
+
* The output keeps the {@link KanbanCard} shape (so the tool contract is stable),
|
|
11
|
+
* with a faithful status/priority mapping. Crucially `urgent ↔ critical`, so a
|
|
12
|
+
* card marked `urgent` becomes a `critical` colab task — which
|
|
13
|
+
* {@link FleetColabStore.isAutoClaimable} refuses to auto-claim. That is the
|
|
14
|
+
* intended safety carry-over: urgent work needs human eyes, it is not silently
|
|
15
|
+
* grabbed by the daemon.
|
|
16
|
+
*
|
|
17
|
+
* Free-form `kanban_link` references (PRs/commits/files) are kept as free-form
|
|
18
|
+
* links — they are NOT folded into the `dependsOn` DAG (which stays a distinct,
|
|
19
|
+
* task-id-only concept surfaced via `buddy autonomy tasks link`).
|
|
20
|
+
*
|
|
21
|
+
* The richer multi-board surface (board registry, assign, archive, unlink, stats)
|
|
22
|
+
* stays on {@link KanbanStore} / the `hermes kanban` CLI — those are not exposed
|
|
23
|
+
* as the 9 `kanban_*` tools, so they are intentionally out of this adapter.
|
|
24
|
+
*/
|
|
25
|
+
import * as path from 'path';
|
|
26
|
+
import { FleetColabStore, } from '../fleet/colab-store.js';
|
|
27
|
+
const STATUS_TO_KANBAN = {
|
|
28
|
+
open: 'todo',
|
|
29
|
+
in_progress: 'in_progress',
|
|
30
|
+
completed: 'done',
|
|
31
|
+
blocked: 'blocked',
|
|
32
|
+
};
|
|
33
|
+
const STATUS_TO_COLAB = {
|
|
34
|
+
todo: 'open',
|
|
35
|
+
in_progress: 'in_progress',
|
|
36
|
+
blocked: 'blocked',
|
|
37
|
+
done: 'completed',
|
|
38
|
+
archived: 'open', // archived has no colab equivalent; treat as open (archive lives on KanbanStore)
|
|
39
|
+
};
|
|
40
|
+
const PRIORITY_TO_KANBAN = {
|
|
41
|
+
critical: 'urgent',
|
|
42
|
+
high: 'high',
|
|
43
|
+
medium: 'medium',
|
|
44
|
+
low: 'low',
|
|
45
|
+
};
|
|
46
|
+
const PRIORITY_TO_COLAB = {
|
|
47
|
+
urgent: 'critical',
|
|
48
|
+
high: 'high',
|
|
49
|
+
medium: 'medium',
|
|
50
|
+
low: 'low',
|
|
51
|
+
};
|
|
52
|
+
export class ColabKanbanAdapter {
|
|
53
|
+
store;
|
|
54
|
+
createId;
|
|
55
|
+
constructor(options = {}) {
|
|
56
|
+
const rootDir = options.rootDir ?? process.cwd();
|
|
57
|
+
const config = { dir: path.join(rootDir, '.codebuddy') };
|
|
58
|
+
if (options.now) {
|
|
59
|
+
const toDate = options.now;
|
|
60
|
+
config.now = () => toDate().getTime();
|
|
61
|
+
}
|
|
62
|
+
this.createId = options.createId;
|
|
63
|
+
if (options.createId) {
|
|
64
|
+
const make = options.createId;
|
|
65
|
+
config.generateId = (prefix) => `${prefix}-${make()}`;
|
|
66
|
+
}
|
|
67
|
+
this.store = new FleetColabStore(config);
|
|
68
|
+
}
|
|
69
|
+
/** The shared board file — now colab-tasks.json, not kanban-board.json. */
|
|
70
|
+
get path() {
|
|
71
|
+
return path.join(this.store.getDir(), 'colab-tasks.json');
|
|
72
|
+
}
|
|
73
|
+
async createCard(input) {
|
|
74
|
+
const title = input.title.trim();
|
|
75
|
+
if (!title)
|
|
76
|
+
throw new Error('title is required');
|
|
77
|
+
const task = this.store.addTask({
|
|
78
|
+
title,
|
|
79
|
+
...(input.id?.trim() ? { id: input.id.trim() } : { id: this.buildCardId(title) }),
|
|
80
|
+
...(input.description?.trim() ? { description: input.description.trim() } : {}),
|
|
81
|
+
...(input.status ? { status: STATUS_TO_COLAB[input.status] } : {}),
|
|
82
|
+
...(input.priority ? { priority: PRIORITY_TO_COLAB[input.priority] } : {}),
|
|
83
|
+
...(input.assignee?.trim() ? { assignee: input.assignee.trim() } : {}),
|
|
84
|
+
...(input.tags && input.tags.length > 0 ? { tags: normalizeTags(input.tags) } : {}),
|
|
85
|
+
});
|
|
86
|
+
return toKanbanCard(task);
|
|
87
|
+
}
|
|
88
|
+
async listCards(filter = {}) {
|
|
89
|
+
const includeDone = filter.includeDone !== false;
|
|
90
|
+
const includeArchived = filter.includeArchived === true || filter.status === 'archived';
|
|
91
|
+
return this.store
|
|
92
|
+
.listTasks()
|
|
93
|
+
.map(toKanbanCard)
|
|
94
|
+
.filter((card) => {
|
|
95
|
+
if (!includeDone && card.status === 'done')
|
|
96
|
+
return false;
|
|
97
|
+
if (!includeArchived && card.status === 'archived')
|
|
98
|
+
return false;
|
|
99
|
+
if (filter.status && card.status !== filter.status)
|
|
100
|
+
return false;
|
|
101
|
+
if (filter.priority && card.priority !== filter.priority)
|
|
102
|
+
return false;
|
|
103
|
+
if (filter.assignee && card.assignee !== filter.assignee)
|
|
104
|
+
return false;
|
|
105
|
+
if (filter.tag && !card.tags.includes(filter.tag))
|
|
106
|
+
return false;
|
|
107
|
+
return true;
|
|
108
|
+
})
|
|
109
|
+
.sort(compareCards);
|
|
110
|
+
}
|
|
111
|
+
async showCard(id) {
|
|
112
|
+
return toKanbanCard(this.getOrThrow(id));
|
|
113
|
+
}
|
|
114
|
+
async completeCard(id, comment, author) {
|
|
115
|
+
this.getOrThrow(id);
|
|
116
|
+
this.store.completeTask(id, { summary: comment?.trim() || 'Completed', ...(author ? { agentId: author } : {}) });
|
|
117
|
+
if (comment?.trim())
|
|
118
|
+
this.store.addComment(id, comment, author);
|
|
119
|
+
return toKanbanCard(this.getOrThrow(id));
|
|
120
|
+
}
|
|
121
|
+
async blockCard(id, reason, author) {
|
|
122
|
+
const trimmed = reason.trim();
|
|
123
|
+
if (!trimmed)
|
|
124
|
+
throw new Error('reason is required');
|
|
125
|
+
this.store.blockTask(id, trimmed);
|
|
126
|
+
this.store.addComment(id, `Blocked: ${trimmed}`, author);
|
|
127
|
+
return toKanbanCard(this.getOrThrow(id));
|
|
128
|
+
}
|
|
129
|
+
async unblockCard(id, comment, author) {
|
|
130
|
+
return toKanbanCard(this.store.unblockTask(id, comment, author));
|
|
131
|
+
}
|
|
132
|
+
async commentCard(id, text, author) {
|
|
133
|
+
return toKanbanCard(this.store.addComment(id, text, author));
|
|
134
|
+
}
|
|
135
|
+
async heartbeatCard(id, message, author) {
|
|
136
|
+
return toKanbanCard(this.store.recordHeartbeat(id, message, author, author));
|
|
137
|
+
}
|
|
138
|
+
async linkCard(id, target, label) {
|
|
139
|
+
return toKanbanCard(this.store.addLink(id, target, label));
|
|
140
|
+
}
|
|
141
|
+
getOrThrow(id) {
|
|
142
|
+
const task = this.store.getTask(id.trim());
|
|
143
|
+
if (!task)
|
|
144
|
+
throw new Error(`kanban card not found: ${id}`);
|
|
145
|
+
return task;
|
|
146
|
+
}
|
|
147
|
+
buildCardId(title) {
|
|
148
|
+
const slug = title.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 48) || 'card';
|
|
149
|
+
const raw = this.createId ? this.createId() : `${process.pid}${process.hrtime.bigint()}`;
|
|
150
|
+
const suffix = raw.replace(/[^a-zA-Z0-9]/g, '').slice(0, 8) || 'card';
|
|
151
|
+
return `kb-${slug}-${suffix}`;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Migrate cards from a legacy `kanban-board.json` (read as {@link KanbanCard}s)
|
|
156
|
+
* into the unified fleet board. Idempotent: a card whose id already exists is
|
|
157
|
+
* skipped, as are archived cards (colab has no archived state). Comments and
|
|
158
|
+
* free-form links are carried over; ephemeral heartbeats are not. Returns the
|
|
159
|
+
* imported / skipped ids so a CLI can report the migration.
|
|
160
|
+
*/
|
|
161
|
+
export function importKanbanCards(cards, store) {
|
|
162
|
+
const imported = [];
|
|
163
|
+
const skipped = [];
|
|
164
|
+
const existing = new Set(store.listTasks().map((t) => t.id));
|
|
165
|
+
for (const card of cards) {
|
|
166
|
+
if (card.status === 'archived' || existing.has(card.id)) {
|
|
167
|
+
skipped.push(card.id);
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
store.addTask({
|
|
171
|
+
id: card.id,
|
|
172
|
+
title: card.title,
|
|
173
|
+
status: STATUS_TO_COLAB[card.status],
|
|
174
|
+
priority: PRIORITY_TO_COLAB[card.priority],
|
|
175
|
+
...(card.description ? { description: card.description } : {}),
|
|
176
|
+
...(card.tags.length > 0 ? { tags: card.tags } : {}),
|
|
177
|
+
...(card.assignee ? { assignee: card.assignee } : {}),
|
|
178
|
+
});
|
|
179
|
+
for (const c of card.comments)
|
|
180
|
+
store.addComment(card.id, c.text, c.author);
|
|
181
|
+
for (const l of card.links)
|
|
182
|
+
store.addLink(card.id, l.target, l.label);
|
|
183
|
+
imported.push(card.id);
|
|
184
|
+
}
|
|
185
|
+
return { imported, skipped };
|
|
186
|
+
}
|
|
187
|
+
function toKanbanCard(task) {
|
|
188
|
+
const createdAt = task.createdAt ?? task.claimedAt ?? new Date(0).toISOString();
|
|
189
|
+
const updatedAt = task.completedAt ?? task.lastHeartbeatAt ?? task.claimedAt ?? task.createdAt ?? createdAt;
|
|
190
|
+
return {
|
|
191
|
+
id: task.id,
|
|
192
|
+
title: task.title,
|
|
193
|
+
status: STATUS_TO_KANBAN[task.status],
|
|
194
|
+
priority: PRIORITY_TO_KANBAN[task.priority],
|
|
195
|
+
tags: normalizeTags(task.tags ?? []),
|
|
196
|
+
links: (task.links ?? []).map((l) => ({
|
|
197
|
+
id: l.id,
|
|
198
|
+
target: l.target,
|
|
199
|
+
createdAt: l.createdAt,
|
|
200
|
+
...(l.label ? { label: l.label } : {}),
|
|
201
|
+
})),
|
|
202
|
+
comments: (task.comments ?? []).map((c) => ({
|
|
203
|
+
id: c.id,
|
|
204
|
+
text: c.text,
|
|
205
|
+
createdAt: c.createdAt,
|
|
206
|
+
...(c.author ? { author: c.author } : {}),
|
|
207
|
+
})),
|
|
208
|
+
heartbeats: (task.heartbeats ?? []).map((h) => ({
|
|
209
|
+
id: h.id,
|
|
210
|
+
createdAt: h.createdAt,
|
|
211
|
+
...(h.message ? { message: h.message } : {}),
|
|
212
|
+
...(h.author ? { author: h.author } : {}),
|
|
213
|
+
})),
|
|
214
|
+
createdAt,
|
|
215
|
+
updatedAt,
|
|
216
|
+
...(task.description ? { description: task.description } : {}),
|
|
217
|
+
...(task.assignee ? { assignee: task.assignee } : {}),
|
|
218
|
+
...(task.blockedReason ? { blockedReason: task.blockedReason } : {}),
|
|
219
|
+
...(task.completedAt ? { completedAt: task.completedAt } : {}),
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
function normalizeTags(tags) {
|
|
223
|
+
return [...new Set(tags.map((t) => t.trim()).filter(Boolean))].sort((a, b) => a.localeCompare(b));
|
|
224
|
+
}
|
|
225
|
+
function compareCards(a, b) {
|
|
226
|
+
const statusOrder = { blocked: 0, in_progress: 1, todo: 2, done: 3, archived: 4 };
|
|
227
|
+
const priorityOrder = { urgent: 0, high: 1, medium: 2, low: 3 };
|
|
228
|
+
return (statusOrder[a.status] - statusOrder[b.status] ||
|
|
229
|
+
priorityOrder[a.priority] - priorityOrder[b.priority] ||
|
|
230
|
+
a.createdAt.localeCompare(b.createdAt));
|
|
231
|
+
}
|
|
232
|
+
//# sourceMappingURL=colab-kanban-adapter.js.map
|
package/dist/plugins/{gitnexus/GitNexusMCPClient.d.ts → code-explorer/CodeExplorerMCPClient.d.ts}
RENAMED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* CodeExplorer MCP Client
|
|
3
3
|
*
|
|
4
|
-
* Talks to a
|
|
4
|
+
* Talks to a CodeExplorer MCP server to query the code graph.
|
|
5
5
|
* Currently operates in **stub mode** — all methods return empty/default
|
|
6
6
|
* results so the rest of the codebase can integrate without requiring
|
|
7
|
-
* a real
|
|
8
|
-
* in a follow-up once
|
|
7
|
+
* a real CodeExplorer installation. The real MCP transport will be wired
|
|
8
|
+
* in a follow-up once code-explorer is available.
|
|
9
9
|
*
|
|
10
|
-
* Tools exposed by
|
|
10
|
+
* Tools exposed by CodeExplorer MCP:
|
|
11
11
|
* - query — natural-language search over the code graph
|
|
12
12
|
* - context — symbol-level call/import graph + process membership
|
|
13
13
|
* - impact — blast-radius analysis (upstream/downstream)
|
|
@@ -75,17 +75,17 @@ export interface GNProcess {
|
|
|
75
75
|
stepIndex: number;
|
|
76
76
|
}>;
|
|
77
77
|
}
|
|
78
|
-
export declare class
|
|
78
|
+
export declare class CodeExplorerMCPClient {
|
|
79
79
|
private repoName;
|
|
80
80
|
private connected;
|
|
81
81
|
constructor(repoName: string);
|
|
82
82
|
/**
|
|
83
|
-
* Connect to the
|
|
83
|
+
* Connect to the CodeExplorer MCP server.
|
|
84
84
|
*
|
|
85
85
|
* In the real implementation this will use `@modelcontextprotocol/sdk`
|
|
86
86
|
* `StdioClientTransport` with:
|
|
87
87
|
* command: 'npx'
|
|
88
|
-
* args: ['-y', '
|
|
88
|
+
* args: ['-y', 'code-explorer@latest', 'mcp']
|
|
89
89
|
*
|
|
90
90
|
* For now this is a stub that marks the client as connected.
|
|
91
91
|
*/
|
|
@@ -114,7 +114,7 @@ export declare class GitNexusMCPClient {
|
|
|
114
114
|
*/
|
|
115
115
|
impact(target: string, direction?: 'upstream' | 'downstream'): Promise<GNImpactResult>;
|
|
116
116
|
/**
|
|
117
|
-
* Execute a raw Cypher query against the
|
|
117
|
+
* Execute a raw Cypher query against the CodeExplorer graph database.
|
|
118
118
|
*/
|
|
119
119
|
cypher(query: string): Promise<unknown[]>;
|
|
120
120
|
/** Get all detected module clusters with cohesion scores. */
|
package/dist/plugins/{gitnexus/GitNexusMCPClient.js → code-explorer/CodeExplorerMCPClient.js}
RENAMED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* CodeExplorer MCP Client
|
|
3
3
|
*
|
|
4
|
-
* Talks to a
|
|
4
|
+
* Talks to a CodeExplorer MCP server to query the code graph.
|
|
5
5
|
* Currently operates in **stub mode** — all methods return empty/default
|
|
6
6
|
* results so the rest of the codebase can integrate without requiring
|
|
7
|
-
* a real
|
|
8
|
-
* in a follow-up once
|
|
7
|
+
* a real CodeExplorer installation. The real MCP transport will be wired
|
|
8
|
+
* in a follow-up once code-explorer is available.
|
|
9
9
|
*
|
|
10
|
-
* Tools exposed by
|
|
10
|
+
* Tools exposed by CodeExplorer MCP:
|
|
11
11
|
* - query — natural-language search over the code graph
|
|
12
12
|
* - context — symbol-level call/import graph + process membership
|
|
13
13
|
* - impact — blast-radius analysis (upstream/downstream)
|
|
@@ -21,32 +21,32 @@
|
|
|
21
21
|
*/
|
|
22
22
|
import { logger } from '../../utils/logger.js';
|
|
23
23
|
// ── Client ──────────────────────────────────────────────────────────
|
|
24
|
-
export class
|
|
24
|
+
export class CodeExplorerMCPClient {
|
|
25
25
|
repoName;
|
|
26
26
|
connected = false;
|
|
27
27
|
constructor(repoName) {
|
|
28
28
|
this.repoName = repoName;
|
|
29
29
|
}
|
|
30
30
|
/**
|
|
31
|
-
* Connect to the
|
|
31
|
+
* Connect to the CodeExplorer MCP server.
|
|
32
32
|
*
|
|
33
33
|
* In the real implementation this will use `@modelcontextprotocol/sdk`
|
|
34
34
|
* `StdioClientTransport` with:
|
|
35
35
|
* command: 'npx'
|
|
36
|
-
* args: ['-y', '
|
|
36
|
+
* args: ['-y', 'code-explorer@latest', 'mcp']
|
|
37
37
|
*
|
|
38
38
|
* For now this is a stub that marks the client as connected.
|
|
39
39
|
*/
|
|
40
40
|
async connect() {
|
|
41
41
|
this.connected = true;
|
|
42
|
-
logger.debug('
|
|
42
|
+
logger.debug('CodeExplorer MCP client connected (stub mode)', {
|
|
43
43
|
repo: this.repoName,
|
|
44
44
|
});
|
|
45
45
|
}
|
|
46
46
|
/** Disconnect from the MCP server. */
|
|
47
47
|
async disconnect() {
|
|
48
48
|
this.connected = false;
|
|
49
|
-
logger.debug('
|
|
49
|
+
logger.debug('CodeExplorer MCP client disconnected', {
|
|
50
50
|
repo: this.repoName,
|
|
51
51
|
});
|
|
52
52
|
}
|
|
@@ -65,7 +65,7 @@ export class GitNexusMCPClient {
|
|
|
65
65
|
*/
|
|
66
66
|
async query(q) {
|
|
67
67
|
this.assertConnected();
|
|
68
|
-
logger.debug('
|
|
68
|
+
logger.debug('CodeExplorer query', { query: q, repo: this.repoName });
|
|
69
69
|
// Stub: real impl calls callTool('query', { query: q, repo: this.repoName })
|
|
70
70
|
return { processes: [], definitions: [] };
|
|
71
71
|
}
|
|
@@ -75,7 +75,7 @@ export class GitNexusMCPClient {
|
|
|
75
75
|
*/
|
|
76
76
|
async context(symbolName) {
|
|
77
77
|
this.assertConnected();
|
|
78
|
-
logger.debug('
|
|
78
|
+
logger.debug('CodeExplorer context', { symbol: symbolName, repo: this.repoName });
|
|
79
79
|
return {
|
|
80
80
|
symbol: {
|
|
81
81
|
uid: symbolName,
|
|
@@ -96,7 +96,7 @@ export class GitNexusMCPClient {
|
|
|
96
96
|
*/
|
|
97
97
|
async impact(target, direction = 'upstream') {
|
|
98
98
|
this.assertConnected();
|
|
99
|
-
logger.debug('
|
|
99
|
+
logger.debug('CodeExplorer impact', { target, direction, repo: this.repoName });
|
|
100
100
|
return {
|
|
101
101
|
target,
|
|
102
102
|
affected: [],
|
|
@@ -105,11 +105,11 @@ export class GitNexusMCPClient {
|
|
|
105
105
|
};
|
|
106
106
|
}
|
|
107
107
|
/**
|
|
108
|
-
* Execute a raw Cypher query against the
|
|
108
|
+
* Execute a raw Cypher query against the CodeExplorer graph database.
|
|
109
109
|
*/
|
|
110
110
|
async cypher(query) {
|
|
111
111
|
this.assertConnected();
|
|
112
|
-
logger.debug('
|
|
112
|
+
logger.debug('CodeExplorer cypher', { query, repo: this.repoName });
|
|
113
113
|
return [];
|
|
114
114
|
}
|
|
115
115
|
// ── Resources ───────────────────────────────────────────────────
|
|
@@ -136,8 +136,8 @@ export class GitNexusMCPClient {
|
|
|
136
136
|
// ── Internal ────────────────────────────────────────────────────
|
|
137
137
|
assertConnected() {
|
|
138
138
|
if (!this.connected) {
|
|
139
|
-
throw new Error('
|
|
139
|
+
throw new Error('CodeExplorerMCPClient is not connected. Call connect() first.');
|
|
140
140
|
}
|
|
141
141
|
}
|
|
142
142
|
}
|
|
143
|
-
//# sourceMappingURL=
|
|
143
|
+
//# sourceMappingURL=CodeExplorerMCPClient.js.map
|
package/dist/plugins/{gitnexus/GitNexusManager.d.ts → code-explorer/CodeExplorerManager.d.ts}
RENAMED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* CodeExplorer Manager
|
|
3
3
|
*
|
|
4
|
-
* Handles
|
|
5
|
-
*
|
|
4
|
+
* Handles CodeExplorer indexing, stats retrieval, and MCP server lifecycle.
|
|
5
|
+
* CodeExplorer provides code graph analysis (symbols, relations, processes, clusters)
|
|
6
6
|
* and exposes them via an MCP server for agent consumption.
|
|
7
7
|
*
|
|
8
8
|
* Usage:
|
|
9
|
-
* const mgr =
|
|
9
|
+
* const mgr = getCodeExplorerManager('/path/to/repo');
|
|
10
10
|
* if (mgr.isInstalled() && !mgr.isRepoIndexed()) {
|
|
11
11
|
* await mgr.analyze();
|
|
12
12
|
* }
|
|
13
13
|
* await mgr.startMCPServer();
|
|
14
14
|
*/
|
|
15
|
-
export interface
|
|
15
|
+
export interface CodeExplorerStats {
|
|
16
16
|
symbols: number;
|
|
17
17
|
relations: number;
|
|
18
18
|
processes: number;
|
|
@@ -20,18 +20,18 @@ export interface GitNexusStats {
|
|
|
20
20
|
indexed: boolean;
|
|
21
21
|
stale: boolean;
|
|
22
22
|
}
|
|
23
|
-
export declare class
|
|
23
|
+
export declare class CodeExplorerManager {
|
|
24
24
|
private repoPath;
|
|
25
25
|
private mcpProcess;
|
|
26
26
|
constructor(repoPath?: string);
|
|
27
|
-
/** Check whether the `
|
|
27
|
+
/** Check whether the `code-explorer` CLI is available on PATH. */
|
|
28
28
|
isInstalled(): boolean;
|
|
29
|
-
/** Check whether the repo has been indexed (`.
|
|
29
|
+
/** Check whether the repo has been indexed (`.codeexplorer/` directory exists). */
|
|
30
30
|
isRepoIndexed(): boolean;
|
|
31
31
|
/**
|
|
32
|
-
* Run `
|
|
32
|
+
* Run `code-explorer analyze` to index the repository.
|
|
33
33
|
*
|
|
34
|
-
* @param options.force - Re-index even if `.
|
|
34
|
+
* @param options.force - Re-index even if `.codeexplorer/` already exists.
|
|
35
35
|
* @param options.withSkills - Also generate skill annotations.
|
|
36
36
|
*/
|
|
37
37
|
analyze(options?: {
|
|
@@ -39,12 +39,12 @@ export declare class GitNexusManager {
|
|
|
39
39
|
withSkills?: boolean;
|
|
40
40
|
}): Promise<void>;
|
|
41
41
|
/**
|
|
42
|
-
* Read stats from `.
|
|
42
|
+
* Read stats from `.codeexplorer/meta.json`.
|
|
43
43
|
* Returns defaults if the index does not exist.
|
|
44
44
|
*/
|
|
45
|
-
getStats():
|
|
45
|
+
getStats(): CodeExplorerStats;
|
|
46
46
|
/**
|
|
47
|
-
* Start the
|
|
47
|
+
* Start the CodeExplorer MCP server as a child process (stdio transport).
|
|
48
48
|
* Only one server is kept alive per manager instance.
|
|
49
49
|
*/
|
|
50
50
|
startMCPServer(): Promise<void>;
|
|
@@ -58,9 +58,9 @@ export declare class GitNexusManager {
|
|
|
58
58
|
dispose(): void;
|
|
59
59
|
}
|
|
60
60
|
/**
|
|
61
|
-
* Get or create a singleton
|
|
61
|
+
* Get or create a singleton CodeExplorerManager for the given repo path.
|
|
62
62
|
* Defaults to `process.cwd()` if no path is provided.
|
|
63
63
|
*/
|
|
64
|
-
export declare function
|
|
64
|
+
export declare function getCodeExplorerManager(repoPath?: string): CodeExplorerManager;
|
|
65
65
|
/** Clear the singleton cache (for testing). */
|
|
66
|
-
export declare function
|
|
66
|
+
export declare function clearCodeExplorerManagerCache(): void;
|