agent-watch 1.0.0
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/CHANGELOG.md +39 -0
- package/LICENSE +21 -0
- package/README.md +95 -0
- package/dist/cli-nMe9-VkJ.d.ts +1 -0
- package/dist/cli.cjs +770 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +771 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/run.d.ts +5 -0
- package/dist/commands/run.d.ts.map +1 -0
- package/dist/config.d.ts +31 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/constants.d.ts +21 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/detect-BMnM34-m.cjs +177 -0
- package/dist/detect-BMnM34-m.cjs.map +1 -0
- package/dist/detect-BWGm1KGQ.js +122 -0
- package/dist/detect-BWGm1KGQ.js.map +1 -0
- package/dist/detect-B_DDBj5N.cjs +182 -0
- package/dist/detect-B_DDBj5N.cjs.map +1 -0
- package/dist/detect-CPW1RRIq.js +117 -0
- package/dist/detect-CPW1RRIq.js.map +1 -0
- package/dist/detect-Dii2e4wf.cjs +174 -0
- package/dist/detect-Dii2e4wf.cjs.map +1 -0
- package/dist/detect-Pkaqn3YG.js +120 -0
- package/dist/detect-Pkaqn3YG.js.map +1 -0
- package/dist/detect.d.ts +16 -0
- package/dist/detect.d.ts.map +1 -0
- package/dist/hooks.d.ts +13 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/index-CXIlEXUx.d.ts +51 -0
- package/dist/index.cjs +13 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/logger-BNjXChov.js +164 -0
- package/dist/logger-BNjXChov.js.map +1 -0
- package/dist/logger-CdAUnlsG.cjs +271 -0
- package/dist/logger-CdAUnlsG.cjs.map +1 -0
- package/dist/logger-hSIaaw_k.js +166 -0
- package/dist/logger-hSIaaw_k.js.map +1 -0
- package/dist/logger-oq2Z7oYf.cjs +269 -0
- package/dist/logger-oq2Z7oYf.cjs.map +1 -0
- package/dist/sessions-90kmJrQI.js +360 -0
- package/dist/sessions-90kmJrQI.js.map +1 -0
- package/dist/sessions-Bmk48zTI.js +311 -0
- package/dist/sessions-Bmk48zTI.js.map +1 -0
- package/dist/sessions-BpNk9YjU.cjs +431 -0
- package/dist/sessions-BpNk9YjU.cjs.map +1 -0
- package/dist/sessions-CkCQikpl.cjs +444 -0
- package/dist/sessions-CkCQikpl.cjs.map +1 -0
- package/dist/sessions-Cy-_zIh6.js +315 -0
- package/dist/sessions-Cy-_zIh6.js.map +1 -0
- package/dist/sessions-DZgPENb6.cjs +434 -0
- package/dist/sessions-DZgPENb6.cjs.map +1 -0
- package/dist/sessions-_HBb3nIW.cjs +495 -0
- package/dist/sessions-_HBb3nIW.cjs.map +1 -0
- package/dist/sessions-tBeR9gKG.js +308 -0
- package/dist/sessions-tBeR9gKG.js.map +1 -0
- package/dist/utils/copilot.d.ts +27 -0
- package/dist/utils/copilot.d.ts.map +1 -0
- package/dist/utils/git.d.ts +35 -0
- package/dist/utils/git.d.ts.map +1 -0
- package/dist/utils/gitignore.d.ts +5 -0
- package/dist/utils/gitignore.d.ts.map +1 -0
- package/dist/utils/logger.d.ts +14 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/sessions.d.ts +48 -0
- package/dist/utils/sessions.d.ts.map +1 -0
- package/package.json +79 -0
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import { existsSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { execSync } from "node:child_process";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import pc from "picocolors";
|
|
6
|
+
|
|
7
|
+
//#region src/constants.ts
|
|
8
|
+
const KNOWN_AGENT_FILES = [
|
|
9
|
+
{
|
|
10
|
+
path: "AGENTS.md",
|
|
11
|
+
label: "AGENTS.md (Recommended)",
|
|
12
|
+
agent: "Generic"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
path: "CLAUDE.md",
|
|
16
|
+
label: "CLAUDE.md (Claude Code)",
|
|
17
|
+
agent: "Claude Code"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
path: ".github/copilot-instructions.md",
|
|
21
|
+
label: ".github/copilot-instructions.md (GitHub Copilot)",
|
|
22
|
+
agent: "GitHub Copilot"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
path: ".cursor/rules",
|
|
26
|
+
label: ".cursor/rules (Cursor)",
|
|
27
|
+
agent: "Cursor"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
path: ".windsurfrules",
|
|
31
|
+
label: ".windsurfrules (Windsurf)",
|
|
32
|
+
agent: "Windsurf"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
path: ".clinerules",
|
|
36
|
+
label: ".clinerules (Cline)",
|
|
37
|
+
agent: "Cline"
|
|
38
|
+
}
|
|
39
|
+
];
|
|
40
|
+
const CONFIG_FILE_NAME = ".agent-watch.json";
|
|
41
|
+
const SUPPORTED_HOOKS = ["commit", "push"];
|
|
42
|
+
const SUPPORTED_AI_AGENTS = [{
|
|
43
|
+
value: "github-copilot-cli",
|
|
44
|
+
name: "GitHub Copilot CLI"
|
|
45
|
+
}];
|
|
46
|
+
const FILE_SELECTION_PAGE_SIZE = 10;
|
|
47
|
+
const SESSIONS_STATE_FILE = ".agent-watch-sessions.json";
|
|
48
|
+
|
|
49
|
+
//#endregion
|
|
50
|
+
//#region src/config.ts
|
|
51
|
+
/**
|
|
52
|
+
* Load the agent-watch configuration from the project root.
|
|
53
|
+
* Returns null if no config file exists or is invalid.
|
|
54
|
+
*/
|
|
55
|
+
function loadConfig(projectRoot) {
|
|
56
|
+
const configPath = join(projectRoot, CONFIG_FILE_NAME);
|
|
57
|
+
if (!existsSync(configPath)) return null;
|
|
58
|
+
try {
|
|
59
|
+
const raw = readFileSync(configPath, "utf-8");
|
|
60
|
+
return JSON.parse(raw);
|
|
61
|
+
} catch {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Save the agent-watch configuration to the project root.
|
|
67
|
+
*/
|
|
68
|
+
function saveConfig(projectRoot, config) {
|
|
69
|
+
writeFileSync(join(projectRoot, CONFIG_FILE_NAME), `${JSON.stringify(config, null, 2)}\n`, "utf-8");
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Create a default configuration with optional overrides.
|
|
73
|
+
*/
|
|
74
|
+
function createDefaultConfig(overrides = {}) {
|
|
75
|
+
return {
|
|
76
|
+
version: 1,
|
|
77
|
+
agentFiles: [],
|
|
78
|
+
watchFileChanges: true,
|
|
79
|
+
includeChatSession: true,
|
|
80
|
+
hookTrigger: "commit",
|
|
81
|
+
agents: [],
|
|
82
|
+
...overrides
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
//#endregion
|
|
87
|
+
//#region src/detect.ts
|
|
88
|
+
/**
|
|
89
|
+
* Scan the project root for known agent configuration files.
|
|
90
|
+
* Returns information about each known file pattern including whether it exists.
|
|
91
|
+
*/
|
|
92
|
+
function detectAgentFiles(projectRoot) {
|
|
93
|
+
return KNOWN_AGENT_FILES.map((pattern) => {
|
|
94
|
+
const absolutePath = join(projectRoot, pattern.path);
|
|
95
|
+
return {
|
|
96
|
+
pattern,
|
|
97
|
+
exists: existsSync(absolutePath),
|
|
98
|
+
absolutePath
|
|
99
|
+
};
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Get only the agent files that exist in the project.
|
|
104
|
+
*/
|
|
105
|
+
function getExistingAgentFiles(projectRoot) {
|
|
106
|
+
return detectAgentFiles(projectRoot).filter((f) => f.exists);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
//#endregion
|
|
110
|
+
//#region src/utils/logger.ts
|
|
111
|
+
const logger = {
|
|
112
|
+
info(message) {
|
|
113
|
+
console.log(pc.blue("info"), message);
|
|
114
|
+
},
|
|
115
|
+
success(message) {
|
|
116
|
+
console.log(pc.green("✔"), message);
|
|
117
|
+
},
|
|
118
|
+
warn(message) {
|
|
119
|
+
console.log(pc.yellow("⚠"), message);
|
|
120
|
+
},
|
|
121
|
+
error(message) {
|
|
122
|
+
console.error(pc.red("✖"), message);
|
|
123
|
+
},
|
|
124
|
+
step(message) {
|
|
125
|
+
console.log(pc.cyan("▸"), message);
|
|
126
|
+
},
|
|
127
|
+
blank() {
|
|
128
|
+
console.log();
|
|
129
|
+
},
|
|
130
|
+
title(message) {
|
|
131
|
+
console.log();
|
|
132
|
+
console.log(pc.bold(pc.magenta(message)));
|
|
133
|
+
console.log();
|
|
134
|
+
},
|
|
135
|
+
banner(message) {
|
|
136
|
+
const border = "═".repeat(message.length + 4);
|
|
137
|
+
console.log();
|
|
138
|
+
console.log(pc.bold(pc.cyan(`╔${border}╗`)));
|
|
139
|
+
console.log(pc.bold(pc.cyan(`║ ${message} ║`)));
|
|
140
|
+
console.log(pc.bold(pc.cyan(`╚${border}╝`)));
|
|
141
|
+
console.log();
|
|
142
|
+
},
|
|
143
|
+
section(message) {
|
|
144
|
+
console.log();
|
|
145
|
+
console.log(pc.bold(pc.blue(`┌─ ${message}`)));
|
|
146
|
+
console.log(pc.bold(pc.blue("│")));
|
|
147
|
+
},
|
|
148
|
+
sectionEnd() {
|
|
149
|
+
console.log(pc.bold(pc.blue("└─")));
|
|
150
|
+
console.log();
|
|
151
|
+
},
|
|
152
|
+
asciiArt() {
|
|
153
|
+
console.log();
|
|
154
|
+
console.log(pc.bold(pc.cyan(" █████╗ ██████╗ ███████╗███╗ ██╗████████╗ ██╗ ██╗ █████╗ ████████╗ ██████╗██╗ ██╗")));
|
|
155
|
+
console.log(pc.bold(pc.cyan(" ██╔══██╗██╔════╝ ██╔════╝████╗ ██║╚══██╔══╝ ██║ ██║██╔══██╗╚══██╔══╝██╔════╝██║ ██║")));
|
|
156
|
+
console.log(pc.bold(pc.magenta(" ███████║██║ ███╗█████╗ ██╔██╗ ██║ ██║ ██║ █╗ ██║███████║ ██║ ██║ ███████║")));
|
|
157
|
+
console.log(pc.bold(pc.magenta(" ██╔══██║██║ ██║██╔══╝ ██║╚██╗██║ ██║ ██║███╗██║██╔══██║ ██║ ██║ ██╔══██║")));
|
|
158
|
+
console.log(pc.bold(pc.blue(" ██║ ██║╚██████╔╝███████╗██║ ╚████║ ██║ ╚███╔███╔╝██║ ██║ ██║ ╚██████╗██║ ██║")));
|
|
159
|
+
console.log(pc.bold(pc.blue(" ╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═══╝ ╚═╝ ╚══╝╚══╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝")));
|
|
160
|
+
console.log();
|
|
161
|
+
console.log(pc.dim(pc.italic(" Keep your AI agent configuration files in sync")));
|
|
162
|
+
console.log();
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
//#endregion
|
|
167
|
+
//#region src/utils/sessions.ts
|
|
168
|
+
const COPILOT_SESSION_STATE_DIR = join(homedir(), ".copilot", "session-state");
|
|
169
|
+
/**
|
|
170
|
+
* Parse a workspace.yaml file into a CopilotSession object.
|
|
171
|
+
* Uses simple line-by-line parsing to avoid a YAML dependency.
|
|
172
|
+
*/
|
|
173
|
+
function parseWorkspaceYaml(content) {
|
|
174
|
+
const result = {};
|
|
175
|
+
for (const line of content.split("\n")) {
|
|
176
|
+
const colonIndex = line.indexOf(":");
|
|
177
|
+
if (colonIndex === -1) continue;
|
|
178
|
+
const key = line.slice(0, colonIndex).trim();
|
|
179
|
+
result[key] = line.slice(colonIndex + 1).trim();
|
|
180
|
+
}
|
|
181
|
+
return {
|
|
182
|
+
id: result.id,
|
|
183
|
+
cwd: result.cwd,
|
|
184
|
+
gitRoot: result.git_root,
|
|
185
|
+
branch: result.branch,
|
|
186
|
+
createdAt: result.created_at,
|
|
187
|
+
updatedAt: result.updated_at
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Discover Copilot CLI sessions for the given project.
|
|
192
|
+
* Reads workspace.yaml from each session in ~/.copilot/session-state/, filters to sessions
|
|
193
|
+
* matching the project's git root, and returns the latest N sessions.
|
|
194
|
+
*/
|
|
195
|
+
function getCopilotSessions(projectRoot, limit = 5) {
|
|
196
|
+
if (!existsSync(COPILOT_SESSION_STATE_DIR)) return [];
|
|
197
|
+
const sessionDirs = readdirSync(COPILOT_SESSION_STATE_DIR, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
198
|
+
const sessions = [];
|
|
199
|
+
for (const dirName of sessionDirs) {
|
|
200
|
+
const workspaceFile = join(COPILOT_SESSION_STATE_DIR, dirName, "workspace.yaml");
|
|
201
|
+
if (!existsSync(workspaceFile)) continue;
|
|
202
|
+
try {
|
|
203
|
+
const parsed = parseWorkspaceYaml(readFileSync(workspaceFile, "utf-8"));
|
|
204
|
+
if (!parsed.id || !parsed.gitRoot) continue;
|
|
205
|
+
if (parsed.gitRoot !== projectRoot && parsed.cwd !== projectRoot) continue;
|
|
206
|
+
sessions.push({
|
|
207
|
+
id: parsed.id,
|
|
208
|
+
cwd: parsed.cwd ?? projectRoot,
|
|
209
|
+
gitRoot: parsed.gitRoot,
|
|
210
|
+
branch: parsed.branch ?? "unknown",
|
|
211
|
+
createdAt: parsed.createdAt ?? "",
|
|
212
|
+
updatedAt: parsed.updatedAt ?? ""
|
|
213
|
+
});
|
|
214
|
+
} catch {}
|
|
215
|
+
}
|
|
216
|
+
sessions.sort((a, b) => {
|
|
217
|
+
const dateA = new Date(a.updatedAt).getTime() || 0;
|
|
218
|
+
return (new Date(b.updatedAt).getTime() || 0) - dateA;
|
|
219
|
+
});
|
|
220
|
+
return sessions.slice(0, limit);
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Read the list of already-processed session IDs from the project state file.
|
|
224
|
+
*/
|
|
225
|
+
function getProcessedSessionIds(projectRoot) {
|
|
226
|
+
const stateFile = join(projectRoot, SESSIONS_STATE_FILE);
|
|
227
|
+
if (!existsSync(stateFile)) return [];
|
|
228
|
+
try {
|
|
229
|
+
const content = readFileSync(stateFile, "utf-8");
|
|
230
|
+
const state = JSON.parse(content);
|
|
231
|
+
return Array.isArray(state.processedSessions) ? state.processedSessions : [];
|
|
232
|
+
} catch {
|
|
233
|
+
return [];
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Save the list of processed session IDs to the project state file.
|
|
238
|
+
*/
|
|
239
|
+
function saveProcessedSessionIds(projectRoot, sessionIds) {
|
|
240
|
+
writeFileSync(join(projectRoot, SESSIONS_STATE_FILE), `${JSON.stringify({ processedSessions: sessionIds }, null, 2)}\n`);
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Get sessions that haven't been processed yet.
|
|
244
|
+
* Returns the latest N sessions for this project, minus already-processed ones.
|
|
245
|
+
*/
|
|
246
|
+
function getUnprocessedSessions(projectRoot, limit = 5) {
|
|
247
|
+
const sessions = getCopilotSessions(projectRoot, limit);
|
|
248
|
+
const processedIds = getProcessedSessionIds(projectRoot);
|
|
249
|
+
return sessions.filter((s) => !processedIds.includes(s.id));
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Export session conversation content by resuming the session with Copilot CLI.
|
|
253
|
+
* Asks Copilot to list all user queries and agent responses from the session.
|
|
254
|
+
*/
|
|
255
|
+
function exportSessionContent(sessionId) {
|
|
256
|
+
try {
|
|
257
|
+
return parseSessionOutput(execSync(`copilot --resume ${sessionId} -p "List every user request and your response from this session. Format each pair on its own line exactly as: USER: <their message> | AGENT: <your response summary>" -s --allow-all`, {
|
|
258
|
+
encoding: "utf-8",
|
|
259
|
+
stdio: "pipe",
|
|
260
|
+
timeout: 12e4
|
|
261
|
+
}));
|
|
262
|
+
} catch (error) {
|
|
263
|
+
logger.warn(`Failed to export session ${sessionId}: ${error instanceof Error ? error.message : String(error)}`);
|
|
264
|
+
return [];
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Parse the copilot output into structured conversation pairs.
|
|
269
|
+
* Expected format per line: USER: <query> | AGENT: <response>
|
|
270
|
+
*/
|
|
271
|
+
function parseSessionOutput(output) {
|
|
272
|
+
const conversations = [];
|
|
273
|
+
const lines = output.split("\n");
|
|
274
|
+
for (const line of lines) {
|
|
275
|
+
const match = /USER:\s*(.+?)\s*\|\s*AGENT:\s*(.+)/.exec(line);
|
|
276
|
+
if (match) conversations.push({
|
|
277
|
+
userQuery: match[1].trim(),
|
|
278
|
+
agentResponse: match[2].trim()
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
return conversations;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Process unprocessed Copilot CLI sessions for a project.
|
|
285
|
+
* Discovers new sessions, extracts conversation context, and marks them as processed.
|
|
286
|
+
* Returns the combined conversation context string for use in agent file updates.
|
|
287
|
+
*/
|
|
288
|
+
function processNewSessions(projectRoot) {
|
|
289
|
+
const unprocessed = getUnprocessedSessions(projectRoot);
|
|
290
|
+
if (unprocessed.length === 0) return null;
|
|
291
|
+
const allConversations = [];
|
|
292
|
+
for (const session of unprocessed) {
|
|
293
|
+
const conversations = exportSessionContent(session.id);
|
|
294
|
+
allConversations.push(...conversations);
|
|
295
|
+
}
|
|
296
|
+
if (allConversations.length === 0) {
|
|
297
|
+
saveProcessedSessionIds(projectRoot, [...getProcessedSessionIds(projectRoot), ...unprocessed.map((s) => s.id)]);
|
|
298
|
+
return null;
|
|
299
|
+
}
|
|
300
|
+
const context = allConversations.map((c) => `User: ${c.userQuery}\nAgent: ${c.agentResponse}`).join("\n\n");
|
|
301
|
+
saveProcessedSessionIds(projectRoot, [...getProcessedSessionIds(projectRoot), ...unprocessed.map((s) => s.id)]);
|
|
302
|
+
logger.success(`Processed ${unprocessed.length} chat session(s)`);
|
|
303
|
+
return context;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
//#endregion
|
|
307
|
+
export { detectAgentFiles as a, loadConfig as c, FILE_SELECTION_PAGE_SIZE as d, KNOWN_AGENT_FILES as f, SUPPORTED_HOOKS as h, logger as i, saveConfig as l, SUPPORTED_AI_AGENTS as m, getUnprocessedSessions as n, getExistingAgentFiles as o, SESSIONS_STATE_FILE as p, processNewSessions as r, createDefaultConfig as s, getCopilotSessions as t, CONFIG_FILE_NAME as u };
|
|
308
|
+
//# sourceMappingURL=sessions-tBeR9gKG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessions-tBeR9gKG.js","names":[],"sources":["../src/constants.ts","../src/config.ts","../src/detect.ts","../src/utils/logger.ts","../src/utils/sessions.ts"],"sourcesContent":["export interface AgentFilePattern {\n\t/** Relative path from project root */\n\tpath: string\n\t/** Human-readable label for display */\n\tlabel: string\n\t/** Which AI agent/tool uses this file */\n\tagent: string\n}\n\nexport const KNOWN_AGENT_FILES: AgentFilePattern[] = [\n\t{ path: \"AGENTS.md\", label: \"AGENTS.md (Recommended)\", agent: \"Generic\" },\n\t{ path: \"CLAUDE.md\", label: \"CLAUDE.md (Claude Code)\", agent: \"Claude Code\" },\n\t{\n\t\tpath: \".github/copilot-instructions.md\",\n\t\tlabel: \".github/copilot-instructions.md (GitHub Copilot)\",\n\t\tagent: \"GitHub Copilot\",\n\t},\n\t{ path: \".cursor/rules\", label: \".cursor/rules (Cursor)\", agent: \"Cursor\" },\n\t{ path: \".windsurfrules\", label: \".windsurfrules (Windsurf)\", agent: \"Windsurf\" },\n\t{ path: \".clinerules\", label: \".clinerules (Cline)\", agent: \"Cline\" },\n]\n\nexport const CONFIG_FILE_NAME = \".agent-watch.json\"\n\nexport const SUPPORTED_HOOKS = [\"commit\", \"push\"] as const\nexport type GitHookTrigger = (typeof SUPPORTED_HOOKS)[number]\n\nexport const SUPPORTED_AI_AGENTS = [{ value: \"github-copilot-cli\", name: \"GitHub Copilot CLI\" }] as const\n\n// UI Configuration\nexport const FILE_SELECTION_PAGE_SIZE = 10 // Static scroll limit for file selection\n\n// Session tracking\nexport const SESSIONS_STATE_FILE = \".agent-watch-sessions.json\"\n","import { existsSync, readFileSync, writeFileSync } from \"node:fs\"\nimport { join } from \"node:path\"\nimport { CONFIG_FILE_NAME, type GitHookTrigger } from \"./constants.js\"\n\nexport interface AgentWatchConfig {\n\t/** Version of the config schema */\n\tversion: 1\n\t/** Relative paths to the agent files being managed */\n\tagentFiles: string[]\n\t/** Whether to use git commit messages and chat sessions for context (legacy) */\n\tuseGitContext?: boolean\n\t/** Whether to watch for file changes */\n\twatchFileChanges: boolean\n\t/** Whether to include chat session context */\n\tincludeChatSession: boolean\n\t/** Which git hook triggers the update */\n\thookTrigger: GitHookTrigger\n\t/** Which AI agent integrations to configure */\n\tagents: string[]\n}\n\n/**\n * Load the agent-watch configuration from the project root.\n * Returns null if no config file exists or is invalid.\n */\nexport function loadConfig(projectRoot: string): AgentWatchConfig | null {\n\tconst configPath = join(projectRoot, CONFIG_FILE_NAME)\n\tif (!existsSync(configPath)) {\n\t\treturn null\n\t}\n\ttry {\n\t\tconst raw = readFileSync(configPath, \"utf-8\")\n\t\treturn JSON.parse(raw) as AgentWatchConfig\n\t} catch {\n\t\treturn null\n\t}\n}\n\n/**\n * Save the agent-watch configuration to the project root.\n */\nexport function saveConfig(projectRoot: string, config: AgentWatchConfig): void {\n\tconst configPath = join(projectRoot, CONFIG_FILE_NAME)\n\twriteFileSync(configPath, `${JSON.stringify(config, null, 2)}\\n`, \"utf-8\")\n}\n\n/**\n * Create a default configuration with optional overrides.\n */\nexport function createDefaultConfig(overrides: Partial<AgentWatchConfig> = {}): AgentWatchConfig {\n\treturn {\n\t\tversion: 1,\n\t\tagentFiles: [],\n\t\twatchFileChanges: true,\n\t\tincludeChatSession: true,\n\t\thookTrigger: \"commit\",\n\t\tagents: [],\n\t\t...overrides,\n\t}\n}\n","import { existsSync } from \"node:fs\"\nimport { join } from \"node:path\"\nimport { type AgentFilePattern, KNOWN_AGENT_FILES } from \"./constants.js\"\n\nexport interface AgentFileInfo {\n\tpattern: AgentFilePattern\n\texists: boolean\n\tabsolutePath: string\n}\n\n/**\n * Scan the project root for known agent configuration files.\n * Returns information about each known file pattern including whether it exists.\n */\nexport function detectAgentFiles(projectRoot: string): AgentFileInfo[] {\n\treturn KNOWN_AGENT_FILES.map((pattern) => {\n\t\tconst absolutePath = join(projectRoot, pattern.path)\n\t\treturn {\n\t\t\tpattern,\n\t\t\texists: existsSync(absolutePath),\n\t\t\tabsolutePath,\n\t\t}\n\t})\n}\n\n/**\n * Get only the agent files that exist in the project.\n */\nexport function getExistingAgentFiles(projectRoot: string): AgentFileInfo[] {\n\treturn detectAgentFiles(projectRoot).filter((f) => f.exists)\n}\n","import pc from \"picocolors\"\n\nexport const logger = {\n\tinfo(message: string): void {\n\t\tconsole.log(pc.blue(\"info\"), message)\n\t},\n\n\tsuccess(message: string): void {\n\t\tconsole.log(pc.green(\"✔\"), message)\n\t},\n\n\twarn(message: string): void {\n\t\tconsole.log(pc.yellow(\"⚠\"), message)\n\t},\n\n\terror(message: string): void {\n\t\tconsole.error(pc.red(\"✖\"), message)\n\t},\n\n\tstep(message: string): void {\n\t\tconsole.log(pc.cyan(\"▸\"), message)\n\t},\n\n\tblank(): void {\n\t\tconsole.log()\n\t},\n\n\ttitle(message: string): void {\n\t\tconsole.log()\n\t\tconsole.log(pc.bold(pc.magenta(message)))\n\t\tconsole.log()\n\t},\n\n\tbanner(message: string): void {\n\t\tconst border = \"═\".repeat(message.length + 4)\n\t\tconsole.log()\n\t\tconsole.log(pc.bold(pc.cyan(`╔${border}╗`)))\n\t\tconsole.log(pc.bold(pc.cyan(`║ ${message} ║`)))\n\t\tconsole.log(pc.bold(pc.cyan(`╚${border}╝`)))\n\t\tconsole.log()\n\t},\n\n\tsection(message: string): void {\n\t\tconsole.log()\n\t\tconsole.log(pc.bold(pc.blue(`┌─ ${message}`)))\n\t\tconsole.log(pc.bold(pc.blue(\"│\")))\n\t},\n\n\tsectionEnd(): void {\n\t\tconsole.log(pc.bold(pc.blue(\"└─\")))\n\t\tconsole.log()\n\t},\n\n\tasciiArt(): void {\n\t\tconsole.log()\n\t\tconsole.log(\n\t\t\tpc.bold(\n\t\t\t\tpc.cyan(\n\t\t\t\t\t\" █████╗ ██████╗ ███████╗███╗ ██╗████████╗ ██╗ ██╗ █████╗ ████████╗ ██████╗██╗ ██╗\",\n\t\t\t\t),\n\t\t\t),\n\t\t)\n\t\tconsole.log(\n\t\t\tpc.bold(\n\t\t\t\tpc.cyan(\n\t\t\t\t\t\" ██╔══██╗██╔════╝ ██╔════╝████╗ ██║╚══██╔══╝ ██║ ██║██╔══██╗╚══██╔══╝██╔════╝██║ ██║\",\n\t\t\t\t),\n\t\t\t),\n\t\t)\n\t\tconsole.log(\n\t\t\tpc.bold(\n\t\t\t\tpc.magenta(\n\t\t\t\t\t\" ███████║██║ ███╗█████╗ ██╔██╗ ██║ ██║ ██║ █╗ ██║███████║ ██║ ██║ ███████║\",\n\t\t\t\t),\n\t\t\t),\n\t\t)\n\t\tconsole.log(\n\t\t\tpc.bold(\n\t\t\t\tpc.magenta(\n\t\t\t\t\t\" ██╔══██║██║ ██║██╔══╝ ██║╚██╗██║ ██║ ██║███╗██║██╔══██║ ██║ ██║ ██╔══██║\",\n\t\t\t\t),\n\t\t\t),\n\t\t)\n\t\tconsole.log(\n\t\t\tpc.bold(\n\t\t\t\tpc.blue(\n\t\t\t\t\t\" ██║ ██║╚██████╔╝███████╗██║ ╚████║ ██║ ╚███╔███╔╝██║ ██║ ██║ ╚██████╗██║ ██║\",\n\t\t\t\t),\n\t\t\t),\n\t\t)\n\t\tconsole.log(\n\t\t\tpc.bold(\n\t\t\t\tpc.blue(\n\t\t\t\t\t\" ╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═══╝ ╚═╝ ╚══╝╚══╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝\",\n\t\t\t\t),\n\t\t\t),\n\t\t)\n\t\tconsole.log()\n\t\tconsole.log(\n\t\t\tpc.dim(pc.italic(\" Keep your AI agent configuration files in sync\")),\n\t\t)\n\t\tconsole.log()\n\t},\n}\n","import { execSync } from \"node:child_process\"\nimport { existsSync, readFileSync, readdirSync, writeFileSync } from \"node:fs\"\nimport { homedir } from \"node:os\"\nimport { join } from \"node:path\"\nimport { SESSIONS_STATE_FILE } from \"../constants.js\"\nimport { logger } from \"./logger.js\"\n\nexport interface CopilotSession {\n\tid: string\n\tcwd: string\n\tgitRoot: string\n\tbranch: string\n\tcreatedAt: string\n\tupdatedAt: string\n}\n\nexport interface SessionConversation {\n\tuserQuery: string\n\tagentResponse: string\n}\n\nconst COPILOT_SESSION_STATE_DIR = join(homedir(), \".copilot\", \"session-state\")\n\n/**\n * Parse a workspace.yaml file into a CopilotSession object.\n * Uses simple line-by-line parsing to avoid a YAML dependency.\n */\nfunction parseWorkspaceYaml(content: string): Partial<CopilotSession> {\n\tconst result: Record<string, string> = {}\n\tfor (const line of content.split(\"\\n\")) {\n\t\tconst colonIndex = line.indexOf(\":\")\n\t\tif (colonIndex === -1) continue\n\t\tconst key = line.slice(0, colonIndex).trim()\n\t\tconst value = line.slice(colonIndex + 1).trim()\n\t\tresult[key] = value\n\t}\n\treturn {\n\t\tid: result.id,\n\t\tcwd: result.cwd,\n\t\tgitRoot: result.git_root,\n\t\tbranch: result.branch,\n\t\tcreatedAt: result.created_at,\n\t\tupdatedAt: result.updated_at,\n\t}\n}\n\n/**\n * Discover Copilot CLI sessions for the given project.\n * Reads workspace.yaml from each session in ~/.copilot/session-state/, filters to sessions\n * matching the project's git root, and returns the latest N sessions.\n */\nexport function getCopilotSessions(projectRoot: string, limit = 5): CopilotSession[] {\n\tif (!existsSync(COPILOT_SESSION_STATE_DIR)) {\n\t\treturn []\n\t}\n\n\tconst sessionDirs = readdirSync(COPILOT_SESSION_STATE_DIR, { withFileTypes: true })\n\t\t.filter((d) => d.isDirectory())\n\t\t.map((d) => d.name)\n\n\tconst sessions: CopilotSession[] = []\n\n\tfor (const dirName of sessionDirs) {\n\t\tconst workspaceFile = join(COPILOT_SESSION_STATE_DIR, dirName, \"workspace.yaml\")\n\t\tif (!existsSync(workspaceFile)) continue\n\n\t\ttry {\n\t\t\tconst content = readFileSync(workspaceFile, \"utf-8\")\n\t\t\tconst parsed = parseWorkspaceYaml(content)\n\n\t\t\tif (!parsed.id || !parsed.gitRoot) continue\n\n\t\t\t// Filter to sessions belonging to this project\n\t\t\tif (parsed.gitRoot !== projectRoot && parsed.cwd !== projectRoot) continue\n\n\t\t\tsessions.push({\n\t\t\t\tid: parsed.id,\n\t\t\t\tcwd: parsed.cwd ?? projectRoot,\n\t\t\t\tgitRoot: parsed.gitRoot,\n\t\t\t\tbranch: parsed.branch ?? \"unknown\",\n\t\t\t\tcreatedAt: parsed.createdAt ?? \"\",\n\t\t\t\tupdatedAt: parsed.updatedAt ?? \"\",\n\t\t\t})\n\t\t} catch {\n\t\t\t// Skip malformed session files\n\t\t}\n\t}\n\n\t// Sort by updatedAt descending (most recent first)\n\tsessions.sort((a, b) => {\n\t\tconst dateA = new Date(a.updatedAt).getTime() || 0\n\t\tconst dateB = new Date(b.updatedAt).getTime() || 0\n\t\treturn dateB - dateA\n\t})\n\n\treturn sessions.slice(0, limit)\n}\n\n/**\n * Read the list of already-processed session IDs from the project state file.\n */\nexport function getProcessedSessionIds(projectRoot: string): string[] {\n\tconst stateFile = join(projectRoot, SESSIONS_STATE_FILE)\n\tif (!existsSync(stateFile)) return []\n\n\ttry {\n\t\tconst content = readFileSync(stateFile, \"utf-8\")\n\t\tconst state = JSON.parse(content)\n\t\treturn Array.isArray(state.processedSessions) ? state.processedSessions : []\n\t} catch {\n\t\treturn []\n\t}\n}\n\n/**\n * Save the list of processed session IDs to the project state file.\n */\nexport function saveProcessedSessionIds(projectRoot: string, sessionIds: string[]): void {\n\tconst stateFile = join(projectRoot, SESSIONS_STATE_FILE)\n\tconst content = JSON.stringify({ processedSessions: sessionIds }, null, 2)\n\twriteFileSync(stateFile, `${content}\\n`)\n}\n\n/**\n * Get sessions that haven't been processed yet.\n * Returns the latest N sessions for this project, minus already-processed ones.\n */\nexport function getUnprocessedSessions(projectRoot: string, limit = 5): CopilotSession[] {\n\tconst sessions = getCopilotSessions(projectRoot, limit)\n\tconst processedIds = getProcessedSessionIds(projectRoot)\n\treturn sessions.filter((s) => !processedIds.includes(s.id))\n}\n\n/**\n * Export session conversation content by resuming the session with Copilot CLI.\n * Asks Copilot to list all user queries and agent responses from the session.\n */\nexport function exportSessionContent(sessionId: string): SessionConversation[] {\n\ttry {\n\t\tconst output = execSync(\n\t\t\t`copilot --resume ${sessionId} -p \"List every user request and your response from this session. Format each pair on its own line exactly as: USER: <their message> | AGENT: <your response summary>\" -s --allow-all`,\n\t\t\t{\n\t\t\t\tencoding: \"utf-8\",\n\t\t\t\tstdio: \"pipe\",\n\t\t\t\ttimeout: 120_000,\n\t\t\t}\n\t\t)\n\t\treturn parseSessionOutput(output)\n\t} catch (error) {\n\t\tlogger.warn(`Failed to export session ${sessionId}: ${error instanceof Error ? error.message : String(error)}`)\n\t\treturn []\n\t}\n}\n\n/**\n * Parse the copilot output into structured conversation pairs.\n * Expected format per line: USER: <query> | AGENT: <response>\n */\nexport function parseSessionOutput(output: string): SessionConversation[] {\n\tconst conversations: SessionConversation[] = []\n\tconst lines = output.split(\"\\n\")\n\n\tfor (const line of lines) {\n\t\tconst match = /USER:\\s*(.+?)\\s*\\|\\s*AGENT:\\s*(.+)/.exec(line)\n\t\tif (match) {\n\t\t\tconversations.push({\n\t\t\t\tuserQuery: match[1].trim(),\n\t\t\t\tagentResponse: match[2].trim(),\n\t\t\t})\n\t\t}\n\t}\n\n\treturn conversations\n}\n\n/**\n * Process unprocessed Copilot CLI sessions for a project.\n * Discovers new sessions, extracts conversation context, and marks them as processed.\n * Returns the combined conversation context string for use in agent file updates.\n */\nexport function processNewSessions(projectRoot: string): string | null {\n\tconst unprocessed = getUnprocessedSessions(projectRoot)\n\n\tif (unprocessed.length === 0) {\n\t\treturn null\n\t}\n\n\tconst allConversations: SessionConversation[] = []\n\n\tfor (const session of unprocessed) {\n\t\tconst conversations = exportSessionContent(session.id)\n\t\tallConversations.push(...conversations)\n\t}\n\n\tif (allConversations.length === 0) {\n\t\t// Still mark as processed to avoid re-checking empty sessions\n\t\tconst processedIds = getProcessedSessionIds(projectRoot)\n\t\tsaveProcessedSessionIds(projectRoot, [...processedIds, ...unprocessed.map((s) => s.id)])\n\t\treturn null\n\t}\n\n\t// Format conversations as context\n\tconst context = allConversations.map((c) => `User: ${c.userQuery}\\nAgent: ${c.agentResponse}`).join(\"\\n\\n\")\n\n\t// Mark sessions as processed\n\tconst processedIds = getProcessedSessionIds(projectRoot)\n\tsaveProcessedSessionIds(projectRoot, [...processedIds, ...unprocessed.map((s) => s.id)])\n\n\tlogger.success(`Processed ${unprocessed.length} chat session(s)`)\n\n\treturn context\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { AgentFileInfo } from "../detect.js";
|
|
2
|
+
interface CopilotStatus {
|
|
3
|
+
installed: boolean;
|
|
4
|
+
authenticated: boolean;
|
|
5
|
+
version?: string;
|
|
6
|
+
username?: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Check the status of GitHub Copilot CLI
|
|
10
|
+
*/
|
|
11
|
+
export declare function checkCopilotStatus(): CopilotStatus;
|
|
12
|
+
/**
|
|
13
|
+
* Setup GitHub Copilot CLI - install and configure if needed
|
|
14
|
+
*/
|
|
15
|
+
export declare function setupGithubCopilotCli(): Promise<boolean>;
|
|
16
|
+
/**
|
|
17
|
+
* Verify GitHub Copilot CLI is properly configured
|
|
18
|
+
*/
|
|
19
|
+
export declare function verifyCopilotSetup(): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Create missing agent files using Copilot CLI.
|
|
22
|
+
* For .github/copilot-instructions.md, uses `copilot init`.
|
|
23
|
+
* For other files, uses `copilot -p` to generate content based on the codebase.
|
|
24
|
+
*/
|
|
25
|
+
export declare function createMissingAgentFiles(projectRoot: string, selectedFiles: string[], detectedFiles: AgentFileInfo[]): void;
|
|
26
|
+
export {};
|
|
27
|
+
//# sourceMappingURL=copilot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"copilot.d.ts","sourceRoot":"","sources":["../../src/utils/copilot.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAGjD,UAAU,aAAa;IACtB,SAAS,EAAE,OAAO,CAAA;IAClB,aAAa,EAAE,OAAO,CAAA;IACtB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAA;CACjB;AA+CD;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,aAAa,CAKlD;AAgCD;;GAEG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,OAAO,CAAC,CAoB9D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,OAAO,CAG5C;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CACtC,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,MAAM,EAAE,EACvB,aAAa,EAAE,aAAa,EAAE,GAC5B,IAAI,CAqCN"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Find the git repository root from a given directory.
|
|
3
|
+
* Returns null if not in a git repository.
|
|
4
|
+
*/
|
|
5
|
+
export declare function findGitRoot(cwd?: string): string | null;
|
|
6
|
+
/**
|
|
7
|
+
* Check if the given directory is inside a git repository.
|
|
8
|
+
*/
|
|
9
|
+
export declare function isGitRepo(cwd?: string): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Get the path to the .git/hooks directory.
|
|
12
|
+
*/
|
|
13
|
+
export declare function getGitHooksDir(gitRoot: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* Check if lefthook is installed in the project.
|
|
16
|
+
*/
|
|
17
|
+
export declare function hasLefthook(projectRoot: string): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Check if husky is installed in the project.
|
|
20
|
+
*/
|
|
21
|
+
export declare function hasHusky(projectRoot: string): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Get the path to lefthook.yml or lefthook.yaml.
|
|
24
|
+
* Returns null if not found.
|
|
25
|
+
*/
|
|
26
|
+
export declare function getLefthookPath(projectRoot: string): string | null;
|
|
27
|
+
/**
|
|
28
|
+
* Check if lefthook CLI is available.
|
|
29
|
+
*/
|
|
30
|
+
export declare function isLefthookAvailable(): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Check if husky CLI is available.
|
|
33
|
+
*/
|
|
34
|
+
export declare function isHuskyAvailable(): boolean;
|
|
35
|
+
//# sourceMappingURL=git.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/utils/git.ts"],"names":[],"mappings":"AAIA;;;GAGG;AACH,wBAAgB,WAAW,CAAC,GAAG,GAAE,MAAsB,GAAG,MAAM,GAAG,IAAI,CAWtE;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,GAAG,GAAE,MAAsB,GAAG,OAAO,CAE9D;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAExD;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAErD;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAWlE;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,OAAO,CAU7C;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAU1C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gitignore.d.ts","sourceRoot":"","sources":["../../src/utils/gitignore.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,CAwC3E"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare const logger: {
|
|
2
|
+
info(message: string): void;
|
|
3
|
+
success(message: string): void;
|
|
4
|
+
warn(message: string): void;
|
|
5
|
+
error(message: string): void;
|
|
6
|
+
step(message: string): void;
|
|
7
|
+
blank(): void;
|
|
8
|
+
title(message: string): void;
|
|
9
|
+
banner(message: string): void;
|
|
10
|
+
section(message: string): void;
|
|
11
|
+
sectionEnd(): void;
|
|
12
|
+
asciiArt(): void;
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,MAAM;kBACJ,MAAM,GAAG,IAAI;qBAIV,MAAM,GAAG,IAAI;kBAIhB,MAAM,GAAG,IAAI;mBAIZ,MAAM,GAAG,IAAI;kBAId,MAAM,GAAG,IAAI;aAIlB,IAAI;mBAIE,MAAM,GAAG,IAAI;oBAMZ,MAAM,GAAG,IAAI;qBASZ,MAAM,GAAG,IAAI;kBAMhB,IAAI;gBAKN,IAAI;CA4BhB,CAAA"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export interface CopilotSession {
|
|
2
|
+
id: string;
|
|
3
|
+
cwd: string;
|
|
4
|
+
gitRoot: string;
|
|
5
|
+
branch: string;
|
|
6
|
+
createdAt: string;
|
|
7
|
+
updatedAt: string;
|
|
8
|
+
}
|
|
9
|
+
export interface SessionConversation {
|
|
10
|
+
userQuery: string;
|
|
11
|
+
agentResponse: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Discover Copilot CLI sessions for the given project.
|
|
15
|
+
* Reads workspace.yaml from each session in ~/.copilot/session-state/, filters to sessions
|
|
16
|
+
* matching the project's git root, and returns the latest N sessions.
|
|
17
|
+
*/
|
|
18
|
+
export declare function getCopilotSessions(projectRoot: string, limit?: number): CopilotSession[];
|
|
19
|
+
/**
|
|
20
|
+
* Read the list of already-processed session IDs from the project state file.
|
|
21
|
+
*/
|
|
22
|
+
export declare function getProcessedSessionIds(projectRoot: string): string[];
|
|
23
|
+
/**
|
|
24
|
+
* Save the list of processed session IDs to the project state file.
|
|
25
|
+
*/
|
|
26
|
+
export declare function saveProcessedSessionIds(projectRoot: string, sessionIds: string[]): void;
|
|
27
|
+
/**
|
|
28
|
+
* Get sessions that haven't been processed yet.
|
|
29
|
+
* Returns the latest N sessions for this project, minus already-processed ones.
|
|
30
|
+
*/
|
|
31
|
+
export declare function getUnprocessedSessions(projectRoot: string, limit?: number): CopilotSession[];
|
|
32
|
+
/**
|
|
33
|
+
* Export session conversation content by resuming the session with Copilot CLI.
|
|
34
|
+
* Asks Copilot to list all user queries and agent responses from the session.
|
|
35
|
+
*/
|
|
36
|
+
export declare function exportSessionContent(sessionId: string): SessionConversation[];
|
|
37
|
+
/**
|
|
38
|
+
* Parse the copilot output into structured conversation pairs.
|
|
39
|
+
* Expected format per line: USER: <query> | AGENT: <response>
|
|
40
|
+
*/
|
|
41
|
+
export declare function parseSessionOutput(output: string): SessionConversation[];
|
|
42
|
+
/**
|
|
43
|
+
* Process unprocessed Copilot CLI sessions for a project.
|
|
44
|
+
* Discovers new sessions, extracts conversation context, and marks them as processed.
|
|
45
|
+
* Returns the combined conversation context string for use in agent file updates.
|
|
46
|
+
*/
|
|
47
|
+
export declare function processNewSessions(projectRoot: string): string | null;
|
|
48
|
+
//# sourceMappingURL=sessions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../../src/utils/sessions.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,cAAc;IAC9B,EAAE,EAAE,MAAM,CAAA;IACV,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,mBAAmB;IACnC,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,MAAM,CAAA;CACrB;AA2BD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,cAAc,EAAE,CA6CnF;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CAWpE;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,IAAI,CAWvF;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,SAAI,GAAG,cAAc,EAAE,CAIvF;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,mBAAmB,EAAE,CAe7E;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,mBAAmB,EAAE,CAexE;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA+BrE"}
|
package/package.json
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "agent-watch",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "CLI tool for keeping AI agent configuration files in sync with your codebase",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"agent-watch": "./dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/index.cjs",
|
|
10
|
+
"module": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
"./package.json": "./package.json",
|
|
14
|
+
".": {
|
|
15
|
+
"import": {
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"default": "./dist/index.js"
|
|
18
|
+
},
|
|
19
|
+
"require": {
|
|
20
|
+
"types": "./dist/index.d.cts",
|
|
21
|
+
"default": "./dist/index.cjs"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "tsdown && tsc -p tsconfig.build.json && cp dist/index.d.ts dist/index.d.cts",
|
|
27
|
+
"prepublishOnly": "pnpm run build && pnpm run test:exports",
|
|
28
|
+
"check": "biome check .",
|
|
29
|
+
"check:fix": "biome check --fix .",
|
|
30
|
+
"test": "pnpm run test:ci",
|
|
31
|
+
"test:ci": "pnpm run check && pnpm run test:types && pnpm run test:unit && pnpm run test:publint",
|
|
32
|
+
"test:unit": "vitest run --passWithNoTests",
|
|
33
|
+
"test:cov": "vitest run --coverage",
|
|
34
|
+
"test:types": "tsc",
|
|
35
|
+
"test:publint": "publint --strict",
|
|
36
|
+
"test:exports": "attw --pack .",
|
|
37
|
+
"test:unused": "knip",
|
|
38
|
+
"changeset": "changeset",
|
|
39
|
+
"release": "changeset publish",
|
|
40
|
+
"version": "changeset version"
|
|
41
|
+
},
|
|
42
|
+
"files": [
|
|
43
|
+
"dist",
|
|
44
|
+
"CHANGELOG.md"
|
|
45
|
+
],
|
|
46
|
+
"author": "",
|
|
47
|
+
"license": "MIT",
|
|
48
|
+
"repository": {
|
|
49
|
+
"type": "git",
|
|
50
|
+
"url": "git+https://github.com/surnr/agent-watch.git"
|
|
51
|
+
},
|
|
52
|
+
"bugs": {
|
|
53
|
+
"url": "https://github.com/surnr/agent-watch/issues"
|
|
54
|
+
},
|
|
55
|
+
"homepage": "https://github.com/surnr/agent-watch#readme",
|
|
56
|
+
"packageManager": "pnpm@10.8.0",
|
|
57
|
+
"engines": {
|
|
58
|
+
"node": ">=20.0.0"
|
|
59
|
+
},
|
|
60
|
+
"dependencies": {
|
|
61
|
+
"@inquirer/prompts": "^7.5.0",
|
|
62
|
+
"commander": "^13.1.0",
|
|
63
|
+
"picocolors": "^1.1.1"
|
|
64
|
+
},
|
|
65
|
+
"devDependencies": {
|
|
66
|
+
"@arethetypeswrong/cli": "^0.17.4",
|
|
67
|
+
"@biomejs/biome": "^1.9.4",
|
|
68
|
+
"@changesets/changelog-github": "^0.5.2",
|
|
69
|
+
"@changesets/cli": "^2.29.0",
|
|
70
|
+
"@types/node": "22.14.1",
|
|
71
|
+
"@vitest/coverage-v8": "^3.1.1",
|
|
72
|
+
"knip": "^5.64.2",
|
|
73
|
+
"lefthook": "^2.1.0",
|
|
74
|
+
"publint": "^0.3.14",
|
|
75
|
+
"tsdown": "^0.15.6",
|
|
76
|
+
"typescript": "^5.8.3",
|
|
77
|
+
"vitest": "^3.1.1"
|
|
78
|
+
}
|
|
79
|
+
}
|