@compilr-dev/cli 0.8.7 → 0.8.8
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/commands-v2/handlers/settings.js +22 -3
- package/dist/commands-v2/types.d.ts +10 -0
- package/dist/compilr-diff-companion.vsix +0 -0
- package/dist/index.js +68 -1
- package/dist/repl-v2.d.ts +19 -1
- package/dist/repl-v2.js +37 -0
- package/dist/ui/overlay-manager.js +10 -2
- package/dist/utils/model-tiers.js +14 -9
- package/package.json +3 -3
- package/dist/episodes/recorder.d.ts +0 -51
- package/dist/episodes/recorder.js +0 -195
- package/dist/episodes/significant-work.d.ts +0 -21
- package/dist/episodes/significant-work.js +0 -56
- package/dist/episodes/store.d.ts +0 -38
- package/dist/episodes/store.js +0 -199
- package/dist/episodes/types.d.ts +0 -35
- package/dist/episodes/types.js +0 -6
- package/dist/episodes/work-at-risk.d.ts +0 -12
- package/dist/episodes/work-at-risk.js +0 -38
- package/dist/episodes/work-summary-anchor.d.ts +0 -23
- package/dist/episodes/work-summary-anchor.js +0 -73
- package/dist/tools/consult.d.ts +0 -14
- package/dist/tools/consult.js +0 -73
- package/dist/tools/delegate-background.d.ts +0 -27
- package/dist/tools/delegate-background.js +0 -115
- package/dist/tools/delegate.d.ts +0 -28
- package/dist/tools/delegate.js +0 -113
- package/dist/tools/handoff.d.ts +0 -17
- package/dist/tools/handoff.js +0 -79
- package/dist/tools/meta-tools.d.ts +0 -31
- package/dist/tools/meta-tools.js +0 -78
- package/dist/ui/overlay/impl/skill-editor-overlay.d.ts +0 -56
- package/dist/ui/overlay/impl/skill-editor-overlay.js +0 -493
package/dist/episodes/store.d.ts
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* File-Based Episode Store
|
|
3
|
-
*
|
|
4
|
-
* Persists work episodes to a JSON file per project.
|
|
5
|
-
* Uses an in-memory cache — all reads are synchronous, writes are async (fire-and-forget).
|
|
6
|
-
*/
|
|
7
|
-
import type { WorkEpisode, ProjectWorkSummary, Effort, EpisodeStore } from '@compilr-dev/agents';
|
|
8
|
-
export interface FileEpisodeStoreOptions {
|
|
9
|
-
/** Path to the episodes JSON file */
|
|
10
|
-
filePath: string;
|
|
11
|
-
/** Maximum episodes to keep (oldest pruned on flush). Default: 1000 */
|
|
12
|
-
maxEpisodes?: number;
|
|
13
|
-
/** Maximum age in milliseconds for cleanup. Default: 30 days */
|
|
14
|
-
maxAgeMs?: number;
|
|
15
|
-
}
|
|
16
|
-
export declare class FileEpisodeStore implements EpisodeStore {
|
|
17
|
-
private readonly filePath;
|
|
18
|
-
private readonly maxEpisodes;
|
|
19
|
-
private readonly maxAgeMs;
|
|
20
|
-
private _episodes;
|
|
21
|
-
private flushPromise;
|
|
22
|
-
constructor(options: FileEpisodeStoreOptions);
|
|
23
|
-
/** Lazy-load and return the in-memory episode cache */
|
|
24
|
-
private get episodes();
|
|
25
|
-
save(episode: WorkEpisode): void;
|
|
26
|
-
saveBatch(episodes: WorkEpisode[]): void;
|
|
27
|
-
getAll(): WorkEpisode[];
|
|
28
|
-
getByFiles(files: string[]): WorkEpisode[];
|
|
29
|
-
getByAgent(agentId: string): WorkEpisode[];
|
|
30
|
-
getBySession(sessionId: string): WorkEpisode[];
|
|
31
|
-
getByTimeRange(start: string, end: string): WorkEpisode[];
|
|
32
|
-
getRecent(count: number): WorkEpisode[];
|
|
33
|
-
getWorkSummary(): ProjectWorkSummary;
|
|
34
|
-
getTotalEffort(episodes?: WorkEpisode[]): Effort;
|
|
35
|
-
cleanup(maxAgeMs?: number): number;
|
|
36
|
-
private loadFromDisk;
|
|
37
|
-
private flush;
|
|
38
|
-
}
|
package/dist/episodes/store.js
DELETED
|
@@ -1,199 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* File-Based Episode Store
|
|
3
|
-
*
|
|
4
|
-
* Persists work episodes to a JSON file per project.
|
|
5
|
-
* Uses an in-memory cache — all reads are synchronous, writes are async (fire-and-forget).
|
|
6
|
-
*/
|
|
7
|
-
import * as fs from 'fs';
|
|
8
|
-
import * as path from 'path';
|
|
9
|
-
import { EFFORT_ORDER } from '@compilr-dev/agents';
|
|
10
|
-
// =============================================================================
|
|
11
|
-
// Constants
|
|
12
|
-
// =============================================================================
|
|
13
|
-
const DEFAULT_MAX_EPISODES = 1000;
|
|
14
|
-
const DEFAULT_MAX_AGE_MS = 30 * 24 * 60 * 60 * 1000; // 30 days
|
|
15
|
-
// =============================================================================
|
|
16
|
-
// Implementation
|
|
17
|
-
// =============================================================================
|
|
18
|
-
export class FileEpisodeStore {
|
|
19
|
-
filePath;
|
|
20
|
-
maxEpisodes;
|
|
21
|
-
maxAgeMs;
|
|
22
|
-
_episodes = null; // lazy-loaded
|
|
23
|
-
flushPromise = null;
|
|
24
|
-
constructor(options) {
|
|
25
|
-
this.filePath = options.filePath;
|
|
26
|
-
this.maxEpisodes = options.maxEpisodes ?? DEFAULT_MAX_EPISODES;
|
|
27
|
-
this.maxAgeMs = options.maxAgeMs ?? DEFAULT_MAX_AGE_MS;
|
|
28
|
-
}
|
|
29
|
-
/** Lazy-load and return the in-memory episode cache */
|
|
30
|
-
get episodes() {
|
|
31
|
-
if (this._episodes === null) {
|
|
32
|
-
this._episodes = this.loadFromDisk();
|
|
33
|
-
}
|
|
34
|
-
return this._episodes;
|
|
35
|
-
}
|
|
36
|
-
// ---------------------------------------------------------------------------
|
|
37
|
-
// Write methods
|
|
38
|
-
// ---------------------------------------------------------------------------
|
|
39
|
-
save(episode) {
|
|
40
|
-
this.episodes.push(episode);
|
|
41
|
-
void this.flush();
|
|
42
|
-
}
|
|
43
|
-
saveBatch(episodes) {
|
|
44
|
-
if (episodes.length === 0)
|
|
45
|
-
return;
|
|
46
|
-
this.episodes.push(...episodes);
|
|
47
|
-
void this.flush();
|
|
48
|
-
}
|
|
49
|
-
// ---------------------------------------------------------------------------
|
|
50
|
-
// Read methods (synchronous — read from cache)
|
|
51
|
-
// ---------------------------------------------------------------------------
|
|
52
|
-
getAll() {
|
|
53
|
-
return [...this.episodes];
|
|
54
|
-
}
|
|
55
|
-
getByFiles(files) {
|
|
56
|
-
const fileSet = new Set(files);
|
|
57
|
-
return this.episodes.filter((ep) => ep.files.some((f) => fileSet.has(f)));
|
|
58
|
-
}
|
|
59
|
-
getByAgent(agentId) {
|
|
60
|
-
return this.episodes.filter((ep) => ep.agentId === agentId);
|
|
61
|
-
}
|
|
62
|
-
getBySession(sessionId) {
|
|
63
|
-
return this.episodes.filter((ep) => ep.sessionId === sessionId);
|
|
64
|
-
}
|
|
65
|
-
getByTimeRange(start, end) {
|
|
66
|
-
return this.episodes.filter((ep) => ep.timestamp >= start && ep.timestamp <= end);
|
|
67
|
-
}
|
|
68
|
-
getRecent(count) {
|
|
69
|
-
return this.episodes.slice(-count);
|
|
70
|
-
}
|
|
71
|
-
getWorkSummary() {
|
|
72
|
-
const eps = this.episodes;
|
|
73
|
-
// Agent breakdown
|
|
74
|
-
const agentMap = new Map();
|
|
75
|
-
for (const ep of eps) {
|
|
76
|
-
const entry = agentMap.get(ep.agentId) ?? { count: 0, maxEffort: 'trivial', timeMs: 0 };
|
|
77
|
-
entry.count++;
|
|
78
|
-
if (EFFORT_ORDER.indexOf(ep.effort) > EFFORT_ORDER.indexOf(entry.maxEffort)) {
|
|
79
|
-
entry.maxEffort = ep.effort;
|
|
80
|
-
}
|
|
81
|
-
entry.timeMs += ep.durationMs ?? 0;
|
|
82
|
-
agentMap.set(ep.agentId, entry);
|
|
83
|
-
}
|
|
84
|
-
// Top files
|
|
85
|
-
const fileCount = new Map();
|
|
86
|
-
for (const ep of eps) {
|
|
87
|
-
for (const f of ep.files) {
|
|
88
|
-
fileCount.set(f, (fileCount.get(f) ?? 0) + 1);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
const topFiles = [...fileCount.entries()]
|
|
92
|
-
.sort((a, b) => b[1] - a[1])
|
|
93
|
-
.slice(0, 10)
|
|
94
|
-
.map(([p, touchCount]) => ({ path: p, touchCount }));
|
|
95
|
-
// Uncommitted work: episodes after last git_commit episode
|
|
96
|
-
let lastCommitIdx = -1;
|
|
97
|
-
for (let i = eps.length - 1; i >= 0; i--) {
|
|
98
|
-
if (eps[i].action === 'commit') {
|
|
99
|
-
lastCommitIdx = i;
|
|
100
|
-
break;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
const uncommittedWork = lastCommitIdx >= 0 ? eps.slice(lastCommitIdx + 1) : [...eps];
|
|
104
|
-
return {
|
|
105
|
-
episodeCount: eps.length,
|
|
106
|
-
totalEffort: this.getTotalEffort(eps),
|
|
107
|
-
timeSpentMs: eps.reduce((sum, ep) => sum + (ep.durationMs ?? 0), 0),
|
|
108
|
-
agentBreakdown: [...agentMap.entries()].map(([agentId, entry]) => ({
|
|
109
|
-
agentId,
|
|
110
|
-
episodeCount: entry.count,
|
|
111
|
-
maxEffort: entry.maxEffort,
|
|
112
|
-
timeSpentMs: entry.timeMs,
|
|
113
|
-
})),
|
|
114
|
-
topFiles,
|
|
115
|
-
uncommittedWork,
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
getTotalEffort(episodes) {
|
|
119
|
-
const eps = episodes ?? this.episodes;
|
|
120
|
-
if (eps.length === 0)
|
|
121
|
-
return 'trivial';
|
|
122
|
-
return maxEffort(eps);
|
|
123
|
-
}
|
|
124
|
-
// ---------------------------------------------------------------------------
|
|
125
|
-
// Cleanup
|
|
126
|
-
// ---------------------------------------------------------------------------
|
|
127
|
-
cleanup(maxAgeMs) {
|
|
128
|
-
const cutoff = new Date(Date.now() - (maxAgeMs ?? this.maxAgeMs)).toISOString();
|
|
129
|
-
const before = this.episodes.length;
|
|
130
|
-
this._episodes = this.episodes.filter((ep) => ep.timestamp > cutoff);
|
|
131
|
-
const removed = before - this._episodes.length;
|
|
132
|
-
if (removed > 0) {
|
|
133
|
-
void this.flush();
|
|
134
|
-
}
|
|
135
|
-
return removed;
|
|
136
|
-
}
|
|
137
|
-
// ---------------------------------------------------------------------------
|
|
138
|
-
// Internal
|
|
139
|
-
// ---------------------------------------------------------------------------
|
|
140
|
-
loadFromDisk() {
|
|
141
|
-
try {
|
|
142
|
-
if (!fs.existsSync(this.filePath))
|
|
143
|
-
return [];
|
|
144
|
-
const raw = fs.readFileSync(this.filePath, 'utf-8');
|
|
145
|
-
const data = JSON.parse(raw);
|
|
146
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- runtime data from disk, version may differ
|
|
147
|
-
if (data.version === 1 && Array.isArray(data.episodes)) {
|
|
148
|
-
return data.episodes;
|
|
149
|
-
}
|
|
150
|
-
return [];
|
|
151
|
-
}
|
|
152
|
-
catch {
|
|
153
|
-
// Corrupted file — start fresh
|
|
154
|
-
return [];
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
async flush() {
|
|
158
|
-
// Coalesce concurrent flushes
|
|
159
|
-
if (this.flushPromise)
|
|
160
|
-
return this.flushPromise;
|
|
161
|
-
this.flushPromise = (async () => {
|
|
162
|
-
try {
|
|
163
|
-
const dir = path.dirname(this.filePath);
|
|
164
|
-
await fs.promises.mkdir(dir, { recursive: true });
|
|
165
|
-
// Prune to max episodes (keep most recent)
|
|
166
|
-
const eps = this.episodes;
|
|
167
|
-
const prunedEpisodes = eps.length > this.maxEpisodes ? eps.slice(-this.maxEpisodes) : eps;
|
|
168
|
-
if (prunedEpisodes !== eps) {
|
|
169
|
-
this._episodes = prunedEpisodes;
|
|
170
|
-
}
|
|
171
|
-
const data = {
|
|
172
|
-
version: 1,
|
|
173
|
-
episodes: prunedEpisodes,
|
|
174
|
-
lastCleanup: new Date().toISOString(),
|
|
175
|
-
};
|
|
176
|
-
await fs.promises.writeFile(this.filePath, JSON.stringify(data, null, 2), 'utf-8');
|
|
177
|
-
}
|
|
178
|
-
catch {
|
|
179
|
-
// Silently fail — episodes are best-effort, never crash the CLI
|
|
180
|
-
}
|
|
181
|
-
finally {
|
|
182
|
-
this.flushPromise = null;
|
|
183
|
-
}
|
|
184
|
-
})();
|
|
185
|
-
return this.flushPromise;
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
// =============================================================================
|
|
189
|
-
// Helpers (module-private)
|
|
190
|
-
// =============================================================================
|
|
191
|
-
function maxEffort(episodes) {
|
|
192
|
-
let max = 'trivial';
|
|
193
|
-
for (const ep of episodes) {
|
|
194
|
-
if (EFFORT_ORDER.indexOf(ep.effort) > EFFORT_ORDER.indexOf(max)) {
|
|
195
|
-
max = ep.effort;
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
return max;
|
|
199
|
-
}
|
package/dist/episodes/types.d.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Episodic Memory Types (CLI)
|
|
3
|
-
*
|
|
4
|
-
* Re-exports core types from @compilr-dev/agents and adds CLI-internal types.
|
|
5
|
-
*/
|
|
6
|
-
export type { Effort, WorkEpisode, EffortSignals, EffortWeights, EffortSummary, ProjectWorkSummary, WorkAtRisk, EpisodeStore, } from '@compilr-dev/agents';
|
|
7
|
-
/**
|
|
8
|
-
* Pending tool signal — accumulated during a batch window before
|
|
9
|
-
* being rolled into a WorkEpisode.
|
|
10
|
-
*/
|
|
11
|
-
export interface PendingToolSignal {
|
|
12
|
-
/** Tool name that was invoked */
|
|
13
|
-
toolName: string;
|
|
14
|
-
/** Files affected by this tool call */
|
|
15
|
-
files: string[];
|
|
16
|
-
/** Lines changed, if known */
|
|
17
|
-
linesChanged: number;
|
|
18
|
-
/** Duration of the tool call in milliseconds */
|
|
19
|
-
durationMs: number;
|
|
20
|
-
/** Whether the tool call succeeded */
|
|
21
|
-
success: boolean;
|
|
22
|
-
/** ISO timestamp of the tool call */
|
|
23
|
-
timestamp: string;
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* On-disk format for the episodes JSON file.
|
|
27
|
-
*/
|
|
28
|
-
export interface EpisodeFile {
|
|
29
|
-
/** File format version */
|
|
30
|
-
version: 1;
|
|
31
|
-
/** All stored episodes */
|
|
32
|
-
episodes: import('@compilr-dev/agents').WorkEpisode[];
|
|
33
|
-
/** ISO timestamp of last cleanup */
|
|
34
|
-
lastCleanup: string;
|
|
35
|
-
}
|
package/dist/episodes/types.js
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Work At Risk Query
|
|
3
|
-
*
|
|
4
|
-
* Pure function to query episodes by affected files and build a WorkAtRisk summary.
|
|
5
|
-
* Used by guardrails and rehearsal to warn about destructive operations.
|
|
6
|
-
*/
|
|
7
|
-
import type { EpisodeStore, WorkAtRisk } from '@compilr-dev/agents';
|
|
8
|
-
/**
|
|
9
|
-
* Query episodes that touch any of the given files and build a WorkAtRisk summary.
|
|
10
|
-
* Returns null if no episodes are at risk.
|
|
11
|
-
*/
|
|
12
|
-
export declare function queryWorkAtRisk(store: EpisodeStore, files: string[]): WorkAtRisk | null;
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Work At Risk Query
|
|
3
|
-
*
|
|
4
|
-
* Pure function to query episodes by affected files and build a WorkAtRisk summary.
|
|
5
|
-
* Used by guardrails and rehearsal to warn about destructive operations.
|
|
6
|
-
*/
|
|
7
|
-
import { EFFORT_ORDER } from '@compilr-dev/agents';
|
|
8
|
-
/**
|
|
9
|
-
* Query episodes that touch any of the given files and build a WorkAtRisk summary.
|
|
10
|
-
* Returns null if no episodes are at risk.
|
|
11
|
-
*/
|
|
12
|
-
export function queryWorkAtRisk(store, files) {
|
|
13
|
-
const episodes = store.getByFiles(files);
|
|
14
|
-
if (episodes.length === 0)
|
|
15
|
-
return null;
|
|
16
|
-
// Max effort
|
|
17
|
-
let totalEffort = 'trivial';
|
|
18
|
-
for (const ep of episodes) {
|
|
19
|
-
if (EFFORT_ORDER.indexOf(ep.effort) > EFFORT_ORDER.indexOf(totalEffort)) {
|
|
20
|
-
totalEffort = ep.effort;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
// Agent attribution: "agentId (terminalPrefix)"
|
|
24
|
-
const seen = new Set();
|
|
25
|
-
const agentAttribution = [];
|
|
26
|
-
for (const ep of episodes) {
|
|
27
|
-
const key = `${ep.agentId} (${ep.terminalPrefix})`;
|
|
28
|
-
if (!seen.has(key)) {
|
|
29
|
-
seen.add(key);
|
|
30
|
-
agentAttribution.push(key);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
// Warning message
|
|
34
|
-
const fileCount = new Set(episodes.flatMap(ep => ep.files)).size;
|
|
35
|
-
const warningMessage = `⚠️ ${String(episodes.length)} episode(s) of work (effort: ${totalEffort}) ` +
|
|
36
|
-
`on ${String(fileCount)} file(s) by ${agentAttribution.join(', ')} would be affected.`;
|
|
37
|
-
return { episodes, totalEffort, agentAttribution, warningMessage };
|
|
38
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Work Summary Anchor
|
|
3
|
-
*
|
|
4
|
-
* Manages an auto-generated anchor that summarizes work done in the current session.
|
|
5
|
-
* Updated automatically after each episode batch is flushed.
|
|
6
|
-
*/
|
|
7
|
-
import type { EpisodeStore, ProjectWorkSummary } from '@compilr-dev/agents';
|
|
8
|
-
import type { AnchorManager } from '@compilr-dev/sdk';
|
|
9
|
-
export interface WorkSummaryAnchorConfig {
|
|
10
|
-
store: EpisodeStore;
|
|
11
|
-
projectId: string;
|
|
12
|
-
getAnchorManager: (projectId: string) => AnchorManager;
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* Build the anchor content string from the store's work summary.
|
|
16
|
-
* Returns null when there are no episodes to summarize.
|
|
17
|
-
*/
|
|
18
|
-
export declare function buildWorkSummaryContent(summary: ProjectWorkSummary): string | null;
|
|
19
|
-
/**
|
|
20
|
-
* Update the work summary anchor for a project.
|
|
21
|
-
* Removes existing anchor (if any) and adds updated one.
|
|
22
|
-
*/
|
|
23
|
-
export declare function updateWorkSummaryAnchor(config: WorkSummaryAnchorConfig): void;
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Work Summary Anchor
|
|
3
|
-
*
|
|
4
|
-
* Manages an auto-generated anchor that summarizes work done in the current session.
|
|
5
|
-
* Updated automatically after each episode batch is flushed.
|
|
6
|
-
*/
|
|
7
|
-
const ANCHOR_TAG = 'episode-work-summary';
|
|
8
|
-
/**
|
|
9
|
-
* Build the anchor content string from the store's work summary.
|
|
10
|
-
* Returns null when there are no episodes to summarize.
|
|
11
|
-
*/
|
|
12
|
-
export function buildWorkSummaryContent(summary) {
|
|
13
|
-
if (summary.episodeCount === 0)
|
|
14
|
-
return null;
|
|
15
|
-
const lines = ['[Session Work Summary]'];
|
|
16
|
-
lines.push(`Episodes: ${String(summary.episodeCount)}, Effort: ${summary.totalEffort}, Time: ${formatMs(summary.timeSpentMs)}`);
|
|
17
|
-
if (summary.agentBreakdown.length > 0) {
|
|
18
|
-
const agents = summary.agentBreakdown
|
|
19
|
-
.map(a => `${a.agentId}(${String(a.episodeCount)})`)
|
|
20
|
-
.join(', ');
|
|
21
|
-
lines.push(`Agents: ${agents}`);
|
|
22
|
-
}
|
|
23
|
-
if (summary.topFiles.length > 0) {
|
|
24
|
-
const files = summary.topFiles.slice(0, 5)
|
|
25
|
-
.map(f => f.path.split('/').pop() ?? f.path)
|
|
26
|
-
.join(', ');
|
|
27
|
-
lines.push(`Top files: ${files}`);
|
|
28
|
-
}
|
|
29
|
-
if (summary.uncommittedWork.length > 0) {
|
|
30
|
-
lines.push(`Uncommitted: ${String(summary.uncommittedWork.length)} episode(s)`);
|
|
31
|
-
}
|
|
32
|
-
return lines.join('\n');
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Update the work summary anchor for a project.
|
|
36
|
-
* Removes existing anchor (if any) and adds updated one.
|
|
37
|
-
*/
|
|
38
|
-
export function updateWorkSummaryAnchor(config) {
|
|
39
|
-
const { store, projectId, getAnchorManager: getManager } = config;
|
|
40
|
-
const manager = getManager(projectId);
|
|
41
|
-
const summary = store.getWorkSummary();
|
|
42
|
-
const content = buildWorkSummaryContent(summary);
|
|
43
|
-
// Remove existing work summary anchor
|
|
44
|
-
const existing = manager.getAll().find((a) => a.tags?.includes(ANCHOR_TAG));
|
|
45
|
-
if (existing)
|
|
46
|
-
manager.remove(existing.id);
|
|
47
|
-
// Add new anchor if there's content
|
|
48
|
-
if (content) {
|
|
49
|
-
manager.add({
|
|
50
|
-
content,
|
|
51
|
-
priority: 'info',
|
|
52
|
-
scope: 'persistent',
|
|
53
|
-
tags: [ANCHOR_TAG],
|
|
54
|
-
projectId,
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Format milliseconds to a human-readable string (e.g., "2h 15m", "45s").
|
|
60
|
-
*/
|
|
61
|
-
function formatMs(ms) {
|
|
62
|
-
if (ms < 1000)
|
|
63
|
-
return `${String(ms)}ms`;
|
|
64
|
-
const totalSeconds = Math.floor(ms / 1000);
|
|
65
|
-
if (totalSeconds < 60)
|
|
66
|
-
return `${String(totalSeconds)}s`;
|
|
67
|
-
const minutes = Math.floor(totalSeconds / 60);
|
|
68
|
-
if (minutes < 60)
|
|
69
|
-
return `${String(minutes)}m`;
|
|
70
|
-
const hours = Math.floor(minutes / 60);
|
|
71
|
-
const remainingMinutes = minutes % 60;
|
|
72
|
-
return remainingMinutes > 0 ? `${String(hours)}h ${String(remainingMinutes)}m` : `${String(hours)}h`;
|
|
73
|
-
}
|
package/dist/tools/consult.d.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Consult Tool — Specialist-to-Specialist Sync Borrow
|
|
3
|
-
*
|
|
4
|
-
* The CLI surfaces the SDK's `consult` semantic via a thin wrapper around
|
|
5
|
-
* the registered consult handler (same pattern as handoff). The actual
|
|
6
|
-
* sub-run of the target agent lives in `handlers/delegation-handlers.ts`.
|
|
7
|
-
*
|
|
8
|
-
* Spec: project-docs/00-requirements/compilr-dev-sdk/consult-tool-spec.md
|
|
9
|
-
*/
|
|
10
|
-
export declare const consultTool: import("@compilr-dev/sdk").Tool<{
|
|
11
|
-
agentId: string;
|
|
12
|
-
question: string;
|
|
13
|
-
context?: string;
|
|
14
|
-
}>;
|
package/dist/tools/consult.js
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Consult Tool — Specialist-to-Specialist Sync Borrow
|
|
3
|
-
*
|
|
4
|
-
* The CLI surfaces the SDK's `consult` semantic via a thin wrapper around
|
|
5
|
-
* the registered consult handler (same pattern as handoff). The actual
|
|
6
|
-
* sub-run of the target agent lives in `handlers/delegation-handlers.ts`.
|
|
7
|
-
*
|
|
8
|
-
* Spec: project-docs/00-requirements/compilr-dev-sdk/consult-tool-spec.md
|
|
9
|
-
*/
|
|
10
|
-
import { defineTool } from '@compilr-dev/sdk';
|
|
11
|
-
import { getConsultHandler } from '../shared-handlers.js';
|
|
12
|
-
export const consultTool = defineTool({
|
|
13
|
-
name: 'consult',
|
|
14
|
-
description: 'Ask another specialist a focused question and continue the conversation yourself. ' +
|
|
15
|
-
'Use this when you need a quick opinion or expertise input from a teammate without giving up the conversation. ' +
|
|
16
|
-
'Unlike handoff, you keep ownership — the target answers and control returns to you. ' +
|
|
17
|
-
'Example: $arch can consult $pm on scope, get an answer, and continue designing.',
|
|
18
|
-
inputSchema: {
|
|
19
|
-
type: 'object',
|
|
20
|
-
properties: {
|
|
21
|
-
agentId: {
|
|
22
|
-
type: 'string',
|
|
23
|
-
description: 'Target specialist agent ID to consult (e.g., "pm", "qa", "dev"). ' +
|
|
24
|
-
'Cannot be yourself or "default" (use handoff to escalate to the coordinator).',
|
|
25
|
-
},
|
|
26
|
-
question: {
|
|
27
|
-
type: 'string',
|
|
28
|
-
description: 'The focused question you want answered — be specific.',
|
|
29
|
-
},
|
|
30
|
-
context: {
|
|
31
|
-
type: 'string',
|
|
32
|
-
description: 'Optional supporting material the target needs to answer well ' +
|
|
33
|
-
'(code snippet, design constraint, current state, etc.).',
|
|
34
|
-
},
|
|
35
|
-
},
|
|
36
|
-
required: ['agentId', 'question'],
|
|
37
|
-
},
|
|
38
|
-
execute: async (input) => {
|
|
39
|
-
if (!input.agentId || input.agentId.trim().length === 0) {
|
|
40
|
-
return { success: false, error: 'agentId is required' };
|
|
41
|
-
}
|
|
42
|
-
if (!input.question || input.question.trim().length === 0) {
|
|
43
|
-
return { success: false, error: 'question is required and cannot be empty' };
|
|
44
|
-
}
|
|
45
|
-
const handler = getConsultHandler();
|
|
46
|
-
if (!handler) {
|
|
47
|
-
return {
|
|
48
|
-
success: false,
|
|
49
|
-
error: 'Consult handler not initialized. This tool requires multi-agent mode with a team.',
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
try {
|
|
53
|
-
const result = await handler({
|
|
54
|
-
agentId: input.agentId.trim(),
|
|
55
|
-
question: input.question.trim(),
|
|
56
|
-
context: input.context?.trim() || undefined,
|
|
57
|
-
});
|
|
58
|
-
if (result.error) {
|
|
59
|
-
return { success: false, error: result.error };
|
|
60
|
-
}
|
|
61
|
-
return {
|
|
62
|
-
success: true,
|
|
63
|
-
result: { answer: result.answer, agentId: result.agentId },
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
catch (err) {
|
|
67
|
-
return {
|
|
68
|
-
success: false,
|
|
69
|
-
error: `Consult failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
},
|
|
73
|
-
});
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Delegate Background Tool - Coordinator Mode
|
|
3
|
-
*
|
|
4
|
-
* Allows the coordinator to delegate tasks to specialists running in background.
|
|
5
|
-
* Unlike the foreground `delegate` tool (which switches the active agent),
|
|
6
|
-
* this tool keeps the coordinator in the foreground while the specialist
|
|
7
|
-
* works independently in the background.
|
|
8
|
-
*
|
|
9
|
-
* Flow:
|
|
10
|
-
* 1. Coordinator calls delegate_background(agentId, task)
|
|
11
|
-
* 2. DelegationTracker registers the delegation
|
|
12
|
-
* 3. Specialist starts as background agent (or queued if busy)
|
|
13
|
-
* 4. Coordinator continues working
|
|
14
|
-
* 5. On completion, coordinator is notified via completion events (Phase 2)
|
|
15
|
-
*/
|
|
16
|
-
interface DelegateBackgroundInput {
|
|
17
|
-
/** Target specialist agent ID (e.g., 'arch', 'dev', 'qa') */
|
|
18
|
-
agentId: string;
|
|
19
|
-
/** Detailed task description for the specialist */
|
|
20
|
-
task: string;
|
|
21
|
-
/** Optional todo index to associate with this delegation */
|
|
22
|
-
todoIndex?: number;
|
|
23
|
-
/** What the specialist should produce (e.g., "architecture document") */
|
|
24
|
-
expectedOutput?: string;
|
|
25
|
-
}
|
|
26
|
-
export declare const delegateBackgroundTool: import("@compilr-dev/sdk").Tool<DelegateBackgroundInput>;
|
|
27
|
-
export {};
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Delegate Background Tool - Coordinator Mode
|
|
3
|
-
*
|
|
4
|
-
* Allows the coordinator to delegate tasks to specialists running in background.
|
|
5
|
-
* Unlike the foreground `delegate` tool (which switches the active agent),
|
|
6
|
-
* this tool keeps the coordinator in the foreground while the specialist
|
|
7
|
-
* works independently in the background.
|
|
8
|
-
*
|
|
9
|
-
* Flow:
|
|
10
|
-
* 1. Coordinator calls delegate_background(agentId, task)
|
|
11
|
-
* 2. DelegationTracker registers the delegation
|
|
12
|
-
* 3. Specialist starts as background agent (or queued if busy)
|
|
13
|
-
* 4. Coordinator continues working
|
|
14
|
-
* 5. On completion, coordinator is notified via completion events (Phase 2)
|
|
15
|
-
*/
|
|
16
|
-
import { defineTool } from '@compilr-dev/sdk';
|
|
17
|
-
import { getDelegateBackgroundHandler } from '../shared-handlers.js';
|
|
18
|
-
// =============================================================================
|
|
19
|
-
// Tool Definition
|
|
20
|
-
// =============================================================================
|
|
21
|
-
export const delegateBackgroundTool = defineTool({
|
|
22
|
-
name: 'delegate_background',
|
|
23
|
-
description: 'Delegate a task to a specialist running in background. ' +
|
|
24
|
-
'The specialist executes independently while you continue working. ' +
|
|
25
|
-
'You will be notified when the specialist completes. ' +
|
|
26
|
-
'Use delegation_status to check progress. ' +
|
|
27
|
-
'Prefer this over delegate — it keeps you in control and tracks status. ' +
|
|
28
|
-
'Only available to the coordinator.',
|
|
29
|
-
inputSchema: {
|
|
30
|
-
type: 'object',
|
|
31
|
-
properties: {
|
|
32
|
-
agentId: {
|
|
33
|
-
type: 'string',
|
|
34
|
-
description: 'Target specialist agent ID (e.g., "arch", "dev", "qa")',
|
|
35
|
-
},
|
|
36
|
-
task: {
|
|
37
|
-
type: 'string',
|
|
38
|
-
description: 'Detailed task description for the specialist',
|
|
39
|
-
},
|
|
40
|
-
todoIndex: {
|
|
41
|
-
type: 'number',
|
|
42
|
-
description: 'Optional todo index to associate with this delegation',
|
|
43
|
-
},
|
|
44
|
-
expectedOutput: {
|
|
45
|
-
type: 'string',
|
|
46
|
-
description: 'What the specialist should produce (e.g., "architecture document", "implementation")',
|
|
47
|
-
},
|
|
48
|
-
},
|
|
49
|
-
required: ['agentId', 'task'],
|
|
50
|
-
},
|
|
51
|
-
execute: async (input) => {
|
|
52
|
-
try {
|
|
53
|
-
// Validate input
|
|
54
|
-
if (!input.agentId || input.agentId.trim().length === 0) {
|
|
55
|
-
return {
|
|
56
|
-
success: false,
|
|
57
|
-
error: 'Agent ID is required',
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
if (!input.task || input.task.trim().length === 0) {
|
|
61
|
-
return {
|
|
62
|
-
success: false,
|
|
63
|
-
error: 'Task description is required',
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
// Prevent self-delegation
|
|
67
|
-
if (input.agentId.trim() === 'default') {
|
|
68
|
-
return {
|
|
69
|
-
success: false,
|
|
70
|
-
error: 'Cannot delegate to yourself (the coordinator). Delegate to a specialist agent.',
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
// Get the delegate background handler from shared-handlers
|
|
74
|
-
const handler = getDelegateBackgroundHandler();
|
|
75
|
-
if (!handler) {
|
|
76
|
-
return {
|
|
77
|
-
success: false,
|
|
78
|
-
error: 'Delegate background handler not initialized. ' +
|
|
79
|
-
'This tool requires multi-agent mode with coordinator role.',
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
// Execute background delegation
|
|
83
|
-
const result = await handler({
|
|
84
|
-
agentId: input.agentId.trim(),
|
|
85
|
-
task: input.task.trim(),
|
|
86
|
-
todoIndex: input.todoIndex,
|
|
87
|
-
expectedOutput: input.expectedOutput?.trim(),
|
|
88
|
-
});
|
|
89
|
-
if (result.error) {
|
|
90
|
-
return {
|
|
91
|
-
success: false,
|
|
92
|
-
error: result.error,
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
const output = {
|
|
96
|
-
delegated: true,
|
|
97
|
-
delegationId: result.delegationId,
|
|
98
|
-
status: result.status,
|
|
99
|
-
message: result.status === 'queued'
|
|
100
|
-
? `Task queued for $${input.agentId} (currently busy). It will start when they're free.`
|
|
101
|
-
: `Task delegated to $${input.agentId} running in background. You'll be notified on completion.`,
|
|
102
|
-
};
|
|
103
|
-
return {
|
|
104
|
-
success: true,
|
|
105
|
-
result: output,
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
catch (err) {
|
|
109
|
-
return {
|
|
110
|
-
success: false,
|
|
111
|
-
error: `Background delegation failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
},
|
|
115
|
-
});
|