@snapback/cli 1.1.15 → 3.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/README.md +50 -12
- package/dist/{auth-HFJRXXG2.js → auth-TDIHGKKL.js} +8 -8
- package/dist/{auto-provision-organization-SF6XM7X4.js → auto-provision-organization-CXHL46P3.js} +4 -4
- package/dist/{chunk-23G5VYA3.js → chunk-CPZWXRP2.js} +196 -23
- package/dist/{chunk-OI2HNNT6.js → chunk-E6V6QKS7.js} +34 -2
- package/dist/{chunk-XYU5FFE3.js → chunk-FMWCFAY7.js} +1 -1
- package/dist/{chunk-HR34NJP7.js → chunk-LIBBDBW5.js} +4 -1
- package/dist/{chunk-DNEADD2G.js → chunk-O7HMAZ7L.js} +10 -12
- package/dist/{chunk-ICKSHS3A.js → chunk-Q4VC7GND.js} +39 -3
- package/dist/{client-WIO6W447.js → client-62E3L6DW.js} +1 -1
- package/dist/{dist-TEWNOZYS.js → dist-5LR7APG5.js} +1 -1
- package/dist/{dist-E7E2T3DQ.js → dist-NFU5UJEW.js} +1 -1
- package/dist/dist-OO5LJHL6.js +12 -0
- package/dist/index.js +492 -321
- package/dist/{local-service-adapter-3JHN6G4O.js → local-service-adapter-AB3UYRUK.js} +1 -1
- package/package.json +3 -3
- package/dist/dist-YZBJAYEJ.js +0 -12
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node --no-warnings=ExperimentalWarning
|
|
2
2
|
import { isSnapbackInitialized, getWorkspaceDir, getViolations, getProtectedFiles, appendSnapbackJsonl, clearCredentials, isLoggedIn, getCredentials, createGlobalDirectory, saveCredentials, getWorkspaceVitals, saveProtectedFiles, getCurrentSession, endCurrentSession, getWorkspaceConfig, recordLearning, getLearnings, readSnapbackJson, recordViolation, writeSnapbackJson, saveCurrentSession, loadSnapbackJsonl, getGlobalConfig, getGlobalDir, saveGlobalConfig, saveWorkspaceConfig } from './chunk-DPWFZNMY.js';
|
|
3
|
-
import { createServiceClient, connectServiceClient, createSessionViaDaemon, endSessionViaDaemon, SnapBackLocalClient, isServiceRunning, getServiceSocketPath, readServicePid, formatDuration, formatBytes, getLogPath, getServicePidPath } from './chunk-
|
|
3
|
+
import { createServiceClient, connectServiceClient, createSessionViaDaemon, endSessionViaDaemon, SnapBackLocalClient, isServiceRunning, getServiceSocketPath, readServicePid, formatDuration, formatBytes, getLogPath, getServicePidPath } from './chunk-O7HMAZ7L.js';
|
|
4
4
|
import { detectOrphans } from './chunk-5SQA44V7.js';
|
|
5
5
|
import './chunk-4YTE4JEW.js';
|
|
6
6
|
import { isRedisAvailable, getCache, setCache } from './chunk-GQ73B37K.js';
|
|
@@ -8,7 +8,7 @@ import { withBreaker, getCircuitBreakerState } from './chunk-PL4HF4M2.js';
|
|
|
8
8
|
import { createLogger, LogLevel, extractErrorCode } from './chunk-WS36HDEU.js';
|
|
9
9
|
import { generateId } from './chunk-5EOPYJ4Y.js';
|
|
10
10
|
import './chunk-CBGOC6RV.js';
|
|
11
|
-
import { detectAIClients, getClient, getSnapbackMCPConfig, writeClientConfig, getServerKey, validateConfig, removeSnapbackConfig, patchApiKeyInClientConfig, detectWorkspaceConfig, validateClientConfig, repairClientConfig, readClientConfig } from './chunk-
|
|
11
|
+
import { detectAIClients, getClient, getSnapbackMCPConfig, writeClientConfig, getServerKey, validateConfig, removeSnapbackConfig, patchApiKeyInClientConfig, detectWorkspaceConfig, validateClientConfig, repairClientConfig, readClientConfig } from './chunk-E6V6QKS7.js';
|
|
12
12
|
import { __commonJS, __name, __require } from './chunk-7ADPL4Q3.js';
|
|
13
13
|
import * as fs32 from 'fs';
|
|
14
14
|
import { existsSync, promises, readFileSync, watch, writeFileSync, statSync, mkdirSync, lstatSync, appendFileSync, renameSync, unlinkSync, openSync, readdirSync, watchFile } from 'fs';
|
|
@@ -66,7 +66,7 @@ var require_package = __commonJS({
|
|
|
66
66
|
"package.json"(exports$1, module) {
|
|
67
67
|
module.exports = {
|
|
68
68
|
name: "@snapback/cli",
|
|
69
|
-
version: "
|
|
69
|
+
version: "3.0.0",
|
|
70
70
|
description: "CLI tool for managing SnapBack snapshots and file protection",
|
|
71
71
|
homepage: "https://snapback.dev",
|
|
72
72
|
repository: {
|
|
@@ -1294,7 +1294,7 @@ var _authPromise = null;
|
|
|
1294
1294
|
async function getAuth() {
|
|
1295
1295
|
if (_auth) return _auth;
|
|
1296
1296
|
if (_authPromise) return _authPromise;
|
|
1297
|
-
_authPromise = import('./auth-
|
|
1297
|
+
_authPromise = import('./auth-TDIHGKKL.js').then((m) => {
|
|
1298
1298
|
_auth = m.auth;
|
|
1299
1299
|
return _auth;
|
|
1300
1300
|
});
|
|
@@ -2900,7 +2900,7 @@ function createWorkspacesCommand() {
|
|
|
2900
2900
|
__name(createWorkspacesCommand, "createWorkspacesCommand");
|
|
2901
2901
|
var version = "0.0.0";
|
|
2902
2902
|
{
|
|
2903
|
-
version = "
|
|
2903
|
+
version = "3.0.0";
|
|
2904
2904
|
}
|
|
2905
2905
|
var client = null;
|
|
2906
2906
|
var connectionPromise = null;
|
|
@@ -3144,7 +3144,7 @@ function displayBox(contentOrOptions, options = {}) {
|
|
|
3144
3144
|
}
|
|
3145
3145
|
__name(displayBox, "displayBox");
|
|
3146
3146
|
function displaySaveStory(riskScore, affectedFiles, snapshotId) {
|
|
3147
|
-
return displayBox(`${chalk37.bold("\u{
|
|
3147
|
+
return displayBox(`${chalk37.bold("\u{1F9E2} SnapBack just protected you!")}
|
|
3148
3148
|
|
|
3149
3149
|
${chalk37.cyan("Risk Score:")} ${chalk37.red(`${riskScore.toFixed(1)}/10`)}
|
|
3150
3150
|
${chalk37.cyan("Files Protected:")} ${chalk37.green(affectedFiles.length.toString())}
|
|
@@ -3160,7 +3160,7 @@ function displaySnapshotSuccess(snapshotId, message, fileCount) {
|
|
|
3160
3160
|
${chalk37.cyan("ID:")} ${snapshotId.substring(0, 8)}
|
|
3161
3161
|
${chalk37.cyan("Message:")} ${message || "(none)"}
|
|
3162
3162
|
${chalk37.cyan("Files:")} ${fileCount} protected`, {
|
|
3163
|
-
title: "\u{
|
|
3163
|
+
title: "\u{1F9E2} SnapBack Protection Active",
|
|
3164
3164
|
type: "success"
|
|
3165
3165
|
});
|
|
3166
3166
|
}
|
|
@@ -3656,7 +3656,7 @@ function findGitRoot(cwd) {
|
|
|
3656
3656
|
__name(findGitRoot, "findGitRoot");
|
|
3657
3657
|
|
|
3658
3658
|
// src/commands/init.ts
|
|
3659
|
-
var cliVersion = "
|
|
3659
|
+
var cliVersion = "3.0.0" ;
|
|
3660
3660
|
function createInitCommand() {
|
|
3661
3661
|
return new Command("init").description("Bootstrap SnapBack for a repository").argument("[path]", "Workspace path (default: current directory)").option("-y, --yes", "Skip confirmation prompts").option("--non-interactive", "Run without prompts (extension/automation)").option("--json", "Output structured JSON result").option("--dry-run", "Show what would be configured").option("--force", "Re-initialize even if already set up").option("--skip-mcp", "Skip MCP configuration").option("--skip-daemon", "Skip daemon registration").option("--api-key <key>", "API key for Pro features").option("--dev", "Use local dev mode for MCP").option("--npm", "Use npm/npx mode for MCP").option("-q, --quiet", "Suppress informational output").option("-v, --verbose", "Show detailed detection reasoning").action(async (path7, options) => {
|
|
3662
3662
|
const result7 = await runInit(path7, options);
|
|
@@ -4297,7 +4297,7 @@ function createStatusCommand() {
|
|
|
4297
4297
|
const cwd = process.cwd();
|
|
4298
4298
|
try {
|
|
4299
4299
|
if (!await isSnapbackInitialized(cwd)) {
|
|
4300
|
-
console.log(chalk37.yellow("SnapBack not initialized in this workspace"));
|
|
4300
|
+
console.log(chalk37.yellow("\u{1F9E2} SnapBack not initialized in this workspace"));
|
|
4301
4301
|
console.log(chalk37.gray("Run: snap init"));
|
|
4302
4302
|
process.exit(1);
|
|
4303
4303
|
}
|
|
@@ -21698,11 +21698,8 @@ z.object({
|
|
|
21698
21698
|
l: z.array(z.string()).optional().describe("Legacy: use 'learnings' instead"),
|
|
21699
21699
|
// Other parameters
|
|
21700
21700
|
notes: z.string().optional().describe("Completion notes"),
|
|
21701
|
-
outcome:
|
|
21702
|
-
|
|
21703
|
-
"abandoned",
|
|
21704
|
-
"blocked"
|
|
21705
|
-
]).optional().describe("Task outcome"),
|
|
21701
|
+
/** Free-text outcome: what was accomplished, what remains, decisions made. ok:0|1 is the primary signal. */
|
|
21702
|
+
outcome: z.string().min(1).max(2e3).optional().describe("Free-text task outcome (what was accomplished, what remains, decisions made)"),
|
|
21706
21703
|
// Efficiency tracking (agent-reported metrics)
|
|
21707
21704
|
efficiency: z.object({
|
|
21708
21705
|
saved: z.string().optional().describe("Tokens saved (e.g., '~12K')"),
|
|
@@ -29296,7 +29293,7 @@ function createLearnCommand() {
|
|
|
29296
29293
|
const cwd = process.cwd();
|
|
29297
29294
|
try {
|
|
29298
29295
|
if (!await isSnapbackInitialized(cwd)) {
|
|
29299
|
-
console.log(chalk37.yellow("SnapBack not initialized in this workspace"));
|
|
29296
|
+
console.log(chalk37.yellow("\u{1F9E2} SnapBack not initialized in this workspace"));
|
|
29300
29297
|
console.log(chalk37.gray("Run: snap init"));
|
|
29301
29298
|
process.exit(1);
|
|
29302
29299
|
}
|
|
@@ -29362,7 +29359,7 @@ function createLearnCommand() {
|
|
|
29362
29359
|
const cwd = process.cwd();
|
|
29363
29360
|
try {
|
|
29364
29361
|
if (!await isSnapbackInitialized(cwd)) {
|
|
29365
|
-
console.log(chalk37.yellow("SnapBack not initialized"));
|
|
29362
|
+
console.log(chalk37.yellow("\u{1F9E2} SnapBack not initialized"));
|
|
29366
29363
|
console.log(chalk37.gray("Run: snap init"));
|
|
29367
29364
|
process.exit(1);
|
|
29368
29365
|
}
|
|
@@ -29871,7 +29868,7 @@ function createSessionCommand() {
|
|
|
29871
29868
|
const cwd = process.cwd();
|
|
29872
29869
|
try {
|
|
29873
29870
|
if (!await isSnapbackInitialized(cwd)) {
|
|
29874
|
-
console.log(chalk37.yellow("SnapBack not initialized in this workspace"));
|
|
29871
|
+
console.log(chalk37.yellow("\u{1F9E2} SnapBack not initialized in this workspace"));
|
|
29875
29872
|
console.log(chalk37.gray("Run: snap init"));
|
|
29876
29873
|
process.exit(1);
|
|
29877
29874
|
}
|
|
@@ -29943,7 +29940,7 @@ function createSessionCommand() {
|
|
|
29943
29940
|
const cwd = process.cwd();
|
|
29944
29941
|
try {
|
|
29945
29942
|
if (!await isSnapbackInitialized(cwd)) {
|
|
29946
|
-
console.log(chalk37.yellow("SnapBack not initialized"));
|
|
29943
|
+
console.log(chalk37.yellow("\u{1F9E2} SnapBack not initialized"));
|
|
29947
29944
|
console.log(chalk37.gray("Run: snap init"));
|
|
29948
29945
|
process.exit(1);
|
|
29949
29946
|
}
|
|
@@ -30007,7 +30004,7 @@ function createSessionCommand() {
|
|
|
30007
30004
|
const cwd = process.cwd();
|
|
30008
30005
|
try {
|
|
30009
30006
|
if (!await isSnapbackInitialized(cwd)) {
|
|
30010
|
-
console.log(chalk37.yellow("SnapBack not initialized"));
|
|
30007
|
+
console.log(chalk37.yellow("\u{1F9E2} SnapBack not initialized"));
|
|
30011
30008
|
console.log(chalk37.gray("Run: snap init"));
|
|
30012
30009
|
process.exit(1);
|
|
30013
30010
|
}
|
|
@@ -30021,7 +30018,7 @@ function createSessionCommand() {
|
|
|
30021
30018
|
] : [];
|
|
30022
30019
|
if (learnings.length > 0 || options.ceremony) {
|
|
30023
30020
|
try {
|
|
30024
|
-
const { createServiceClient: createServiceClient4, connectServiceClient: connectServiceClient2, isServiceRunning: isServiceRunning2 } = await import('./local-service-adapter-
|
|
30021
|
+
const { createServiceClient: createServiceClient4, connectServiceClient: connectServiceClient2, isServiceRunning: isServiceRunning2 } = await import('./local-service-adapter-AB3UYRUK.js');
|
|
30025
30022
|
if (isServiceRunning2()) {
|
|
30026
30023
|
const client2 = createServiceClient4();
|
|
30027
30024
|
await connectServiceClient2(client2);
|
|
@@ -30068,7 +30065,7 @@ function createSessionCommand() {
|
|
|
30068
30065
|
const cwd = process.cwd();
|
|
30069
30066
|
try {
|
|
30070
30067
|
if (!await isSnapbackInitialized(cwd)) {
|
|
30071
|
-
console.log(chalk37.yellow("SnapBack not initialized"));
|
|
30068
|
+
console.log(chalk37.yellow("\u{1F9E2} SnapBack not initialized"));
|
|
30072
30069
|
console.log(chalk37.gray("Run: snap init"));
|
|
30073
30070
|
process.exit(1);
|
|
30074
30071
|
}
|
|
@@ -34670,10 +34667,10 @@ async function autoStartDaemon2() {
|
|
|
34670
34667
|
try {
|
|
34671
34668
|
if (existsSync(SERVICE_SOCKET_PATH2)) {
|
|
34672
34669
|
try {
|
|
34673
|
-
await new Promise((
|
|
34670
|
+
await new Promise((resolve62, reject) => {
|
|
34674
34671
|
const sock = createConnection(SERVICE_SOCKET_PATH2, () => {
|
|
34675
34672
|
sock.end();
|
|
34676
|
-
|
|
34673
|
+
resolve62();
|
|
34677
34674
|
});
|
|
34678
34675
|
sock.on("error", reject);
|
|
34679
34676
|
setTimeout(() => {
|
|
@@ -34745,10 +34742,10 @@ async function autoStartDaemon2() {
|
|
|
34745
34742
|
while (Date.now() < deadline) {
|
|
34746
34743
|
if (existsSync(SERVICE_SOCKET_PATH2)) {
|
|
34747
34744
|
try {
|
|
34748
|
-
await new Promise((
|
|
34745
|
+
await new Promise((resolve62, reject) => {
|
|
34749
34746
|
const sock = createConnection(SERVICE_SOCKET_PATH2, () => {
|
|
34750
34747
|
sock.end();
|
|
34751
|
-
|
|
34748
|
+
resolve62();
|
|
34752
34749
|
});
|
|
34753
34750
|
sock.on("error", reject);
|
|
34754
34751
|
setTimeout(() => {
|
|
@@ -36675,11 +36672,8 @@ var init_validation = __esm2({
|
|
|
36675
36672
|
l: z.array(z.string()).optional().describe("Legacy: use 'learnings' instead"),
|
|
36676
36673
|
// Other parameters
|
|
36677
36674
|
notes: z.string().optional().describe("Completion notes"),
|
|
36678
|
-
outcome:
|
|
36679
|
-
|
|
36680
|
-
"abandoned",
|
|
36681
|
-
"blocked"
|
|
36682
|
-
]).optional().describe("Task outcome"),
|
|
36675
|
+
/** Free-text outcome: what was accomplished, what remains, decisions made. ok:0|1 is the primary signal. */
|
|
36676
|
+
outcome: z.string().min(1).max(2e3).optional().describe("Free-text task outcome (what was accomplished, what remains, decisions made)"),
|
|
36683
36677
|
// Efficiency tracking (agent-reported metrics)
|
|
36684
36678
|
efficiency: z.object({
|
|
36685
36679
|
saved: z.string().optional().describe("Tokens saved (e.g., '~12K')"),
|
|
@@ -41880,12 +41874,6 @@ var init_begin_task = __esm2({
|
|
|
41880
41874
|
const keywords = providedKeywords ?? extractKeywords2(task);
|
|
41881
41875
|
const daemonResult = await beginTaskViaService(workspaceRoot, task, files, keywords);
|
|
41882
41876
|
if (daemonResult) {
|
|
41883
|
-
await startTaskAsync(workspaceRoot, {
|
|
41884
|
-
description: task,
|
|
41885
|
-
plannedFiles: files || [],
|
|
41886
|
-
snapshotId: daemonResult.snapshot.id,
|
|
41887
|
-
keywords
|
|
41888
|
-
});
|
|
41889
41877
|
const observations2 = (await drainPendingObservationsAsync(workspaceRoot)).map((o) => ({
|
|
41890
41878
|
type: o.type,
|
|
41891
41879
|
message: o.message
|
|
@@ -41970,7 +41958,9 @@ var init_begin_task = __esm2({
|
|
|
41970
41958
|
return result2(formatCompactText(formatCompactResult(output2, gitContext2)));
|
|
41971
41959
|
}
|
|
41972
41960
|
const learningsCount = daemonResult.learnings?.length || 0;
|
|
41973
|
-
const
|
|
41961
|
+
const daemonSnapshotCreated = daemonResult.snapshot?.created ?? (daemonResult.snapshotIds?.length ?? 0) > 0;
|
|
41962
|
+
const daemonSnapshotId = daemonResult.snapshot?.id ?? daemonResult.snapshotIds?.[0];
|
|
41963
|
+
const hint2 = daemonSnapshotCreated ? `Safety snapshot created via daemon: ${daemonSnapshotId}` : learningsCount > 0 ? `${learningsCount} relevant learning(s) found` : "Ready to start coding! (daemon-coordinated)";
|
|
41974
41964
|
return result2(JSON.stringify({
|
|
41975
41965
|
...output2,
|
|
41976
41966
|
message: `Task started: ${task}`,
|
|
@@ -42190,12 +42180,7 @@ var init_begin_task = __esm2({
|
|
|
42190
42180
|
snapshotId: snapshot.id,
|
|
42191
42181
|
keywords
|
|
42192
42182
|
});
|
|
42193
|
-
|
|
42194
|
-
return result2(JSON.stringify({
|
|
42195
|
-
error: "E002_TASK_START_FAILED",
|
|
42196
|
-
message: "Failed to start task. Please try again."
|
|
42197
|
-
}), true);
|
|
42198
|
-
}
|
|
42183
|
+
const taskId = currentTask?.id ?? `local_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 6)}`;
|
|
42199
42184
|
const state = await getSessionStateAsync(workspaceRoot);
|
|
42200
42185
|
if (state) {
|
|
42201
42186
|
state.riskAreasTouched = riskAssessment.riskAreas;
|
|
@@ -42208,7 +42193,7 @@ var init_begin_task = __esm2({
|
|
|
42208
42193
|
const nextActions = generateNextActions(riskAssessment, learnings, staticAnalysis);
|
|
42209
42194
|
const proactive_guidance = await generateProactiveGuidance(files || []);
|
|
42210
42195
|
const output = {
|
|
42211
|
-
taskId
|
|
42196
|
+
taskId,
|
|
42212
42197
|
intent,
|
|
42213
42198
|
intentHints,
|
|
42214
42199
|
snapshot,
|
|
@@ -43815,7 +43800,7 @@ var init_error_pattern_registry = __esm2({
|
|
|
43815
43800
|
});
|
|
43816
43801
|
async function runCommand2(cmd, args, cwd, timeoutMs = 3e4) {
|
|
43817
43802
|
const start = Date.now();
|
|
43818
|
-
return new Promise((
|
|
43803
|
+
return new Promise((resolve62) => {
|
|
43819
43804
|
let stdout = "";
|
|
43820
43805
|
let stderr = "";
|
|
43821
43806
|
let killed = false;
|
|
@@ -43840,7 +43825,7 @@ async function runCommand2(cmd, args, cwd, timeoutMs = 3e4) {
|
|
|
43840
43825
|
});
|
|
43841
43826
|
proc.on("close", (code) => {
|
|
43842
43827
|
clearTimeout(timeout);
|
|
43843
|
-
|
|
43828
|
+
resolve62({
|
|
43844
43829
|
exitCode: killed ? -1 : code ?? 0,
|
|
43845
43830
|
stdout,
|
|
43846
43831
|
stderr,
|
|
@@ -43849,7 +43834,7 @@ async function runCommand2(cmd, args, cwd, timeoutMs = 3e4) {
|
|
|
43849
43834
|
});
|
|
43850
43835
|
proc.on("error", (err22) => {
|
|
43851
43836
|
clearTimeout(timeout);
|
|
43852
|
-
|
|
43837
|
+
resolve62({
|
|
43853
43838
|
exitCode: -1,
|
|
43854
43839
|
stdout,
|
|
43855
43840
|
stderr: err22.message,
|
|
@@ -44524,7 +44509,7 @@ var init_review_work = __esm2({
|
|
|
44524
44509
|
}
|
|
44525
44510
|
});
|
|
44526
44511
|
function getFileDiff(file, workspaceRoot) {
|
|
44527
|
-
return new Promise((
|
|
44512
|
+
return new Promise((resolve62) => {
|
|
44528
44513
|
try {
|
|
44529
44514
|
const result7 = execSync(`git diff --no-color -- "${file}"`, {
|
|
44530
44515
|
cwd: workspaceRoot,
|
|
@@ -44532,15 +44517,15 @@ function getFileDiff(file, workspaceRoot) {
|
|
|
44532
44517
|
maxBuffer: 1024 * 1024,
|
|
44533
44518
|
timeout: 5e3
|
|
44534
44519
|
});
|
|
44535
|
-
|
|
44520
|
+
resolve62(result7.trim() || null);
|
|
44536
44521
|
} catch {
|
|
44537
|
-
|
|
44522
|
+
resolve62(null);
|
|
44538
44523
|
}
|
|
44539
44524
|
});
|
|
44540
44525
|
}
|
|
44541
44526
|
__name(getFileDiff, "getFileDiff");
|
|
44542
44527
|
function getGitChanges(workspaceRoot) {
|
|
44543
|
-
return new Promise((
|
|
44528
|
+
return new Promise((resolve62) => {
|
|
44544
44529
|
const changes = /* @__PURE__ */ new Map();
|
|
44545
44530
|
try {
|
|
44546
44531
|
const result7 = execSync("git status --porcelain", {
|
|
@@ -44564,7 +44549,7 @@ function getGitChanges(workspaceRoot) {
|
|
|
44564
44549
|
}
|
|
44565
44550
|
} catch {
|
|
44566
44551
|
}
|
|
44567
|
-
|
|
44552
|
+
resolve62(changes);
|
|
44568
44553
|
});
|
|
44569
44554
|
}
|
|
44570
44555
|
__name(getGitChanges, "getGitChanges");
|
|
@@ -46683,16 +46668,7 @@ var init_input_sanitization = __esm2({
|
|
|
46683
46668
|
// Intent - reuse guide intent schema
|
|
46684
46669
|
intent: GuideIntentSchema.optional()
|
|
46685
46670
|
});
|
|
46686
|
-
SnapEndOutcomeSchema =
|
|
46687
|
-
"completed",
|
|
46688
|
-
"abandoned",
|
|
46689
|
-
"blocked",
|
|
46690
|
-
"partial",
|
|
46691
|
-
"success",
|
|
46692
|
-
"failure",
|
|
46693
|
-
"error",
|
|
46694
|
-
"timeout"
|
|
46695
|
-
]);
|
|
46671
|
+
SnapEndOutcomeSchema = sanitizedString(2e3, "outcome");
|
|
46696
46672
|
SnapEndParamsSchema = z.object({
|
|
46697
46673
|
// Success flag - boolean only
|
|
46698
46674
|
ok: z.union([
|
|
@@ -46977,8 +46953,7 @@ var init_errors3 = __esm2({
|
|
|
46977
46953
|
});
|
|
46978
46954
|
function isActiveSession(session) {
|
|
46979
46955
|
if (!session) return false;
|
|
46980
|
-
return session.active === true || session.state === "active" || session.state === "started" ||
|
|
46981
|
-
!!session.id && session.state !== "ended" && session.state !== "idle";
|
|
46956
|
+
return session.active === true || session.state === "active" || session.state === "started" || session.state === "running";
|
|
46982
46957
|
}
|
|
46983
46958
|
__name(isActiveSession, "isActiveSession");
|
|
46984
46959
|
var init_session = __esm2({
|
|
@@ -47017,6 +46992,11 @@ function formatSnapbackResponse(data) {
|
|
|
47017
46992
|
if (data.isResumed) {
|
|
47018
46993
|
sections.push(`Session resumed for: ${data.task}`);
|
|
47019
46994
|
sections.push("Continuing from previous work in this workspace.");
|
|
46995
|
+
if (data.sessionVitals) {
|
|
46996
|
+
const { filesModified, snapshotsCreated, learningsCaptured } = data.sessionVitals;
|
|
46997
|
+
sections.push(`
|
|
46998
|
+
\u{1F4CA} **Session state**: ${filesModified} files modified | ${snapshotsCreated} snapshots | ${learningsCaptured} learnings captured`);
|
|
46999
|
+
}
|
|
47020
47000
|
} else {
|
|
47021
47001
|
sections.push(`Session started for: ${data.task}`);
|
|
47022
47002
|
}
|
|
@@ -47081,12 +47061,8 @@ function formatSnapbackResponse(data) {
|
|
|
47081
47061
|
if (data.beginData?.context?.coChangePatternsInScope && data.beginData.context.coChangePatternsInScope.length > 0) {
|
|
47082
47062
|
hasTier2 = true;
|
|
47083
47063
|
tier2Sections.push("\n### Files That Change Together\n");
|
|
47084
|
-
for (const
|
|
47085
|
-
|
|
47086
|
-
for (let j = i + 1; j < cluster.files.length; j++) {
|
|
47087
|
-
tier2Sections.push(`- When you modify **${cluster.files[i]}**, you usually need to update **${cluster.files[j]}** too (${Math.round(cluster.coOccurrenceRate * 100)}% confidence)`);
|
|
47088
|
-
}
|
|
47089
|
-
}
|
|
47064
|
+
for (const pair of data.beginData.context.coChangePatternsInScope) {
|
|
47065
|
+
tier2Sections.push(`- When you modify **${pair.source}**, you usually need to update **${pair.target}** too (${Math.round(pair.confidence * 100)}% confidence)`);
|
|
47090
47066
|
}
|
|
47091
47067
|
}
|
|
47092
47068
|
if (data.contextData?.learnings && data.contextData.learnings.length > 0) {
|
|
@@ -47135,13 +47111,9 @@ function formatSnapbackResponse(data) {
|
|
|
47135
47111
|
}
|
|
47136
47112
|
const riskCoChanges = extractCoChangeAlerts(data.riskAssessment);
|
|
47137
47113
|
const beginCoChangePairs = /* @__PURE__ */ new Set();
|
|
47138
|
-
for (const
|
|
47139
|
-
|
|
47140
|
-
|
|
47141
|
-
beginCoChangePairs.add(`${cluster.files[i]}:${cluster.files[j]}`);
|
|
47142
|
-
beginCoChangePairs.add(`${cluster.files[j]}:${cluster.files[i]}`);
|
|
47143
|
-
}
|
|
47144
|
-
}
|
|
47114
|
+
for (const pair of data.beginData?.context?.coChangePatternsInScope ?? []) {
|
|
47115
|
+
beginCoChangePairs.add(`${pair.source}:${pair.target}`);
|
|
47116
|
+
beginCoChangePairs.add(`${pair.target}:${pair.source}`);
|
|
47145
47117
|
}
|
|
47146
47118
|
const newCoChanges = riskCoChanges.filter((c) => !beginCoChangePairs.has(`${c.file}:${c.partner}`));
|
|
47147
47119
|
if (newCoChanges.length > 0) {
|
|
@@ -47166,6 +47138,7 @@ function formatSnapbackResponse(data) {
|
|
|
47166
47138
|
}
|
|
47167
47139
|
}
|
|
47168
47140
|
sections.push("\nSnapBack is now monitoring this session. Files are automatically snapshotted when risk pressure exceeds 70% \u2014 no manual action needed.");
|
|
47141
|
+
sections.push('\n\u26A0\uFE0F **REQUIRED**: Call `snap_end({ outcome: "completed" })` when your task is complete to close the session and capture learnings.');
|
|
47169
47142
|
const kbNote = learningCount > 0 ? ` [${learningCount} learnings in knowledge base]` : " [session 1 \u2014 building baseline]";
|
|
47170
47143
|
sections.push(`
|
|
47171
47144
|
[session: ${data.session.id ?? data.session.taskId}]${kbNote}`);
|
|
@@ -47265,12 +47238,23 @@ ${validation.errors?.join("\n") ?? "Unknown validation error"}`
|
|
|
47265
47238
|
task: validatedData?.task ?? validatedData?.t ?? args.task ?? "",
|
|
47266
47239
|
files: validatedData?.files ?? validatedData?.f
|
|
47267
47240
|
};
|
|
47241
|
+
if (!params.task?.trim()) {
|
|
47242
|
+
return {
|
|
47243
|
+
content: [
|
|
47244
|
+
{
|
|
47245
|
+
type: "text",
|
|
47246
|
+
text: "Error: task parameter is required and cannot be empty."
|
|
47247
|
+
}
|
|
47248
|
+
],
|
|
47249
|
+
isError: true
|
|
47250
|
+
};
|
|
47251
|
+
}
|
|
47268
47252
|
const workspace = resolveWorkspace(context);
|
|
47269
47253
|
const daemon = getDaemonClient2(workspace);
|
|
47270
|
-
const health = await checkDaemonHealth(daemon, workspace);
|
|
47271
47254
|
let session = null;
|
|
47272
47255
|
let isResumed = false;
|
|
47273
47256
|
let beginData = null;
|
|
47257
|
+
let sessionVitals = null;
|
|
47274
47258
|
try {
|
|
47275
47259
|
const currentSession = await daemon.request("session/current", {
|
|
47276
47260
|
workspace
|
|
@@ -47284,13 +47268,16 @@ ${validation.errors?.join("\n") ?? "Unknown validation error"}`
|
|
|
47284
47268
|
};
|
|
47285
47269
|
isResumed = true;
|
|
47286
47270
|
if (params.task && params.task !== session.task) {
|
|
47271
|
+
const previousTask = session.task;
|
|
47272
|
+
session.task = params.task;
|
|
47287
47273
|
try {
|
|
47288
47274
|
await daemon.request("session/update-task", {
|
|
47289
47275
|
workspace,
|
|
47290
47276
|
task: params.task
|
|
47291
47277
|
});
|
|
47292
|
-
|
|
47293
|
-
|
|
47278
|
+
} catch (updateError) {
|
|
47279
|
+
session.task = previousTask;
|
|
47280
|
+
console.error("[snapback] session/update-task failed, task name reverted:", updateError);
|
|
47294
47281
|
}
|
|
47295
47282
|
}
|
|
47296
47283
|
try {
|
|
@@ -47303,24 +47290,31 @@ ${validation.errors?.join("\n") ?? "Unknown validation error"}`
|
|
|
47303
47290
|
const warningsResult = await daemon.request("intelligence/warnings", {
|
|
47304
47291
|
workspace
|
|
47305
47292
|
});
|
|
47306
|
-
const coChangeClusters = (coChangeResult?.pairs ?? []).map((pair) => ({
|
|
47307
|
-
files: [
|
|
47308
|
-
pair.source,
|
|
47309
|
-
pair.target
|
|
47310
|
-
],
|
|
47311
|
-
coOccurrenceRate: pair.confidence,
|
|
47312
|
-
sampleSize: 1
|
|
47313
|
-
}));
|
|
47314
47293
|
beginData = {
|
|
47315
47294
|
id: currentSession.id,
|
|
47316
47295
|
context: {
|
|
47317
47296
|
fragileFilesInScope: fragileResult?.files ?? [],
|
|
47318
|
-
coChangePatternsInScope:
|
|
47297
|
+
coChangePatternsInScope: coChangeResult?.pairs ?? [],
|
|
47319
47298
|
concurrentSessionWarning: null
|
|
47320
47299
|
},
|
|
47321
47300
|
learnings: []
|
|
47322
47301
|
};
|
|
47323
47302
|
console.error(`[snapback] Resume intelligence: ${fragileResult?.files?.length ?? 0} fragile, ${coChangeResult?.pairs?.length ?? 0} co-changes, ${warningsResult?.warnings?.length ?? 0} warnings`);
|
|
47303
|
+
try {
|
|
47304
|
+
const vitalsResult = await daemon.request("session/vitals", {
|
|
47305
|
+
workspace
|
|
47306
|
+
});
|
|
47307
|
+
if (vitalsResult) {
|
|
47308
|
+
sessionVitals = {
|
|
47309
|
+
filesModified: vitalsResult.filesModified ?? 0,
|
|
47310
|
+
snapshotsCreated: vitalsResult.snapshotsCreated ?? 0,
|
|
47311
|
+
learningsCaptured: vitalsResult.learningsCaptured ?? 0
|
|
47312
|
+
};
|
|
47313
|
+
console.error(`[snapback] Resume vitals: ${sessionVitals.filesModified} files, ${sessionVitals.snapshotsCreated} snapshots, ${sessionVitals.learningsCaptured} learnings`);
|
|
47314
|
+
}
|
|
47315
|
+
} catch (vitalsError) {
|
|
47316
|
+
console.error("[snapback] Resume vitals fetch failed:", vitalsError);
|
|
47317
|
+
}
|
|
47324
47318
|
} catch (resumeError) {
|
|
47325
47319
|
console.error("[snapback] Resume intelligence fetch failed:", resumeError);
|
|
47326
47320
|
}
|
|
@@ -47385,6 +47379,7 @@ ${validation.errors?.join("\n") ?? "Unknown validation error"}`
|
|
|
47385
47379
|
}
|
|
47386
47380
|
}
|
|
47387
47381
|
if (!session) {
|
|
47382
|
+
const health = await checkDaemonHealth(daemon, workspace);
|
|
47388
47383
|
if (!health.healthy) {
|
|
47389
47384
|
return formatDaemonError(classifyDaemonError(null));
|
|
47390
47385
|
}
|
|
@@ -47425,7 +47420,8 @@ ${validation.errors?.join("\n") ?? "Unknown validation error"}`
|
|
|
47425
47420
|
riskAssessment,
|
|
47426
47421
|
contextData,
|
|
47427
47422
|
beginData,
|
|
47428
|
-
task: params.task
|
|
47423
|
+
task: params.task,
|
|
47424
|
+
sessionVitals
|
|
47429
47425
|
});
|
|
47430
47426
|
}, "handleSnapback");
|
|
47431
47427
|
__name12(formatSnapbackResponse, "formatSnapbackResponse");
|
|
@@ -47433,42 +47429,34 @@ ${validation.errors?.join("\n") ?? "Unknown validation error"}`
|
|
|
47433
47429
|
__name12(extractFragileFiles, "extractFragileFiles");
|
|
47434
47430
|
__name12(extractCoChangeAlerts, "extractCoChangeAlerts");
|
|
47435
47431
|
snapbackTool = {
|
|
47436
|
-
name: "
|
|
47437
|
-
description: `Call this
|
|
47438
|
-
|
|
47439
|
-
WHAT IT DOES:
|
|
47440
|
-
- Opens a monitored session that tracks every file you modify
|
|
47441
|
-
- Returns relevant learnings from previous sessions, fragile file warnings, and co-change patterns
|
|
47442
|
-
- When risk pressure exceeds 70%, snapshots are created automatically (checked every 30s)
|
|
47443
|
-
- If a session already exists for this workspace, resumes it with fresh intelligence
|
|
47444
|
-
- On first session (cold start): briefing will be sparse \u2014 intelligence accumulates with each session
|
|
47432
|
+
name: "snap_begin",
|
|
47433
|
+
description: `Call this before any other tool when starting a coding task. Opens a session, returns codebase intelligence your context window does not have \u2014 fragile files, co-change patterns, past learnings \u2014 and enables automatic snapshot protection. Without this call: no session tracking, no automatic snapshots, snap_pulse and snap_end return empty results.
|
|
47445
47434
|
|
|
47446
47435
|
WHEN TO CALL:
|
|
47447
|
-
- At the start of
|
|
47448
|
-
- Safe to call multiple times \u2014 resumes the existing session
|
|
47436
|
+
- At the start of every task, before any file reads or changes \u2014 even if you don't know which files yet
|
|
47437
|
+
- Safe to call multiple times \u2014 resumes the existing session without creating duplicates
|
|
47449
47438
|
|
|
47450
47439
|
WHEN NOT TO CALL:
|
|
47451
|
-
- Mid-task
|
|
47452
|
-
-
|
|
47453
|
-
-
|
|
47440
|
+
- Mid-task health check \u2192 snap_pulse
|
|
47441
|
+
- Record a discovery \u2192 snap_learn
|
|
47442
|
+
- Close the session \u2192 snap_end
|
|
47454
47443
|
|
|
47455
|
-
WORKFLOW
|
|
47456
|
-
snapback \u2192 [your work] \u2192 snap_pulse (optional, mid-task) \u2192 snapback_end
|
|
47457
|
-
snapback_learn can be called anytime during the session.
|
|
47444
|
+
WORKFLOW: snap_begin \u2192 [work] \u2192 snap_learn (anytime) \u2192 snap_pulse (optional) \u2192 snap_end
|
|
47458
47445
|
|
|
47459
47446
|
WHAT YOU GET BACK (natural language text, not structured JSON):
|
|
47460
47447
|
- Session ID and status (new or resumed)
|
|
47461
47448
|
- Relevant learnings from previous sessions in this codebase
|
|
47462
47449
|
- Fragile files in scope (files with high rollback or violation history)
|
|
47463
47450
|
- Co-change patterns (files that historically change together)
|
|
47464
|
-
-
|
|
47451
|
+
- Risk assessment for the files you intend to modify
|
|
47465
47452
|
- Session health vitals (pulse, pressure, trajectory)
|
|
47466
47453
|
|
|
47467
|
-
|
|
47468
|
-
|
|
47454
|
+
AUTOMATIC PROTECTION: Snapshots fire when risk pressure exceeds 70%. No manual action needed.
|
|
47455
|
+
|
|
47456
|
+
COLD START: First session returns sparse output. Intelligence accumulates with each session \u2014 call it anyway.
|
|
47469
47457
|
|
|
47470
47458
|
EXAMPLE:
|
|
47471
|
-
snapback({ task: "
|
|
47459
|
+
snapback({ task: "Refactor auth middleware to support OAuth2 PKCE", files: ["src/auth/middleware.ts"] })`,
|
|
47472
47460
|
inputSchema: {
|
|
47473
47461
|
type: "object",
|
|
47474
47462
|
properties: {
|
|
@@ -47500,7 +47488,7 @@ Example: ["src/auth/middleware.ts", "src/auth/oauth.ts"]`
|
|
|
47500
47488
|
t: {
|
|
47501
47489
|
type: "string",
|
|
47502
47490
|
title: "Task Description (shorthand)",
|
|
47503
|
-
description: "
|
|
47491
|
+
description: "Shorthand for 'task'."
|
|
47504
47492
|
},
|
|
47505
47493
|
f: {
|
|
47506
47494
|
type: "array",
|
|
@@ -47508,7 +47496,7 @@ Example: ["src/auth/middleware.ts", "src/auth/oauth.ts"]`
|
|
|
47508
47496
|
type: "string"
|
|
47509
47497
|
},
|
|
47510
47498
|
title: "Target Files (shorthand)",
|
|
47511
|
-
description: "
|
|
47499
|
+
description: "Shorthand for 'files'."
|
|
47512
47500
|
}
|
|
47513
47501
|
},
|
|
47514
47502
|
required: [],
|
|
@@ -47527,7 +47515,7 @@ Example: ["src/auth/middleware.ts", "src/auth/oauth.ts"]`
|
|
|
47527
47515
|
},
|
|
47528
47516
|
annotations: {
|
|
47529
47517
|
title: "\u{1F6E1}\uFE0F SnapBack",
|
|
47530
|
-
readOnlyHint:
|
|
47518
|
+
readOnlyHint: false,
|
|
47531
47519
|
destructiveHint: false,
|
|
47532
47520
|
idempotentHint: true,
|
|
47533
47521
|
openWorldHint: true
|
|
@@ -47536,6 +47524,25 @@ Example: ["src/auth/middleware.ts", "src/auth/oauth.ts"]`
|
|
|
47536
47524
|
};
|
|
47537
47525
|
}
|
|
47538
47526
|
});
|
|
47527
|
+
function formatDuration22(ms) {
|
|
47528
|
+
const seconds = Math.floor(ms / 1e3);
|
|
47529
|
+
const minutes = Math.floor(seconds / 60);
|
|
47530
|
+
const hours = Math.floor(minutes / 60);
|
|
47531
|
+
if (hours > 0) {
|
|
47532
|
+
const remainingMinutes = minutes % 60;
|
|
47533
|
+
return `${hours}h ${remainingMinutes}m`;
|
|
47534
|
+
}
|
|
47535
|
+
if (minutes > 0) {
|
|
47536
|
+
return `${minutes}m`;
|
|
47537
|
+
}
|
|
47538
|
+
return `${seconds}s`;
|
|
47539
|
+
}
|
|
47540
|
+
__name(formatDuration22, "formatDuration2");
|
|
47541
|
+
var init_format_duration = __esm2({
|
|
47542
|
+
"src/utils/format-duration.ts"() {
|
|
47543
|
+
__name12(formatDuration22, "formatDuration");
|
|
47544
|
+
}
|
|
47545
|
+
});
|
|
47539
47546
|
function formatEndResponse(data) {
|
|
47540
47547
|
const sections = [];
|
|
47541
47548
|
const task = data.session.task ?? data.session.metadata?.task ?? "unnamed task";
|
|
@@ -47626,10 +47633,6 @@ _Estimated time saved: ~${timeSaved} minutes_`);
|
|
|
47626
47633
|
tier2Sections.push(`- ${typeEmoji} **${insight.title}**: ${insight.body}`);
|
|
47627
47634
|
}
|
|
47628
47635
|
}
|
|
47629
|
-
if (data.aiSynthesis.nextAction) {
|
|
47630
|
-
tier2Sections.push(`
|
|
47631
|
-
**Recommended next action**: ${data.aiSynthesis.nextAction}`);
|
|
47632
|
-
}
|
|
47633
47636
|
if (data.aiSynthesis.model) {
|
|
47634
47637
|
tier2Sections.push(`
|
|
47635
47638
|
_Analysis powered by ${data.aiSynthesis.model}${data.aiSynthesis.cached ? " (cached)" : ""}_`);
|
|
@@ -47689,20 +47692,6 @@ ${metrics.snapshotsCreated} snapshots were created during this session. If any c
|
|
|
47689
47692
|
};
|
|
47690
47693
|
}
|
|
47691
47694
|
__name(formatEndResponse, "formatEndResponse");
|
|
47692
|
-
function formatDuration22(ms) {
|
|
47693
|
-
const seconds = Math.floor(ms / 1e3);
|
|
47694
|
-
const minutes = Math.floor(seconds / 60);
|
|
47695
|
-
const hours = Math.floor(minutes / 60);
|
|
47696
|
-
if (hours > 0) {
|
|
47697
|
-
const remainingMinutes = minutes % 60;
|
|
47698
|
-
return `${hours}h ${remainingMinutes}m`;
|
|
47699
|
-
}
|
|
47700
|
-
if (minutes > 0) {
|
|
47701
|
-
return `${minutes}m`;
|
|
47702
|
-
}
|
|
47703
|
-
return `${seconds}s`;
|
|
47704
|
-
}
|
|
47705
|
-
__name(formatDuration22, "formatDuration2");
|
|
47706
47695
|
function buildSessionDiagnostics(session, workspace) {
|
|
47707
47696
|
const lines = [
|
|
47708
47697
|
"**Diagnostic Info:**"
|
|
@@ -47728,10 +47717,10 @@ function buildSessionDiagnostics(session, workspace) {
|
|
|
47728
47717
|
return lines.join("\n");
|
|
47729
47718
|
}
|
|
47730
47719
|
__name(buildSessionDiagnostics, "buildSessionDiagnostics");
|
|
47731
|
-
function gatherMetrics(endResult, ceremonyData,
|
|
47732
|
-
const filesModified = ceremonyData?.filesModified ??
|
|
47733
|
-
const snapshotsCreated = ceremonyData?.
|
|
47734
|
-
const learningsCaptured = ceremonyData?.learningsCaptured ??
|
|
47720
|
+
function gatherMetrics(endResult, ceremonyData, _session) {
|
|
47721
|
+
const filesModified = ceremonyData?.filesModified ?? endResult?.filesModified ?? 0;
|
|
47722
|
+
const snapshotsCreated = ceremonyData?.checkpointsCreated ?? ceremonyData?.snapshotsCreated ?? endResult?.snapshotsCreated ?? 0;
|
|
47723
|
+
const learningsCaptured = ceremonyData?.learningsCaptured ?? endResult?.learningsCaptured ?? 0;
|
|
47735
47724
|
const duration = ceremonyData?.duration ?? ceremonyData?.ceremony?.summary?.duration ?? endResult?.duration ?? null;
|
|
47736
47725
|
return {
|
|
47737
47726
|
filesModified,
|
|
@@ -47754,10 +47743,12 @@ function describeCoherence(score) {
|
|
|
47754
47743
|
};
|
|
47755
47744
|
return descriptions[score] ?? `Unknown coherence level: ${score}`;
|
|
47756
47745
|
}
|
|
47757
|
-
|
|
47758
|
-
if (
|
|
47759
|
-
if (
|
|
47760
|
-
if (
|
|
47746
|
+
let normalizedScore = score;
|
|
47747
|
+
if (typeof normalizedScore === "number" && normalizedScore > 1) normalizedScore = normalizedScore / 100;
|
|
47748
|
+
if (normalizedScore >= 0.8) return "Highly focused session \u2014 changes were well-scoped and consistent";
|
|
47749
|
+
if (normalizedScore >= 0.6) return "Good coherence \u2014 mostly focused with minor tangents";
|
|
47750
|
+
if (normalizedScore >= 0.4) return "Mixed session \u2014 several distinct concerns addressed";
|
|
47751
|
+
if (normalizedScore >= 0.2) return "Scattered session \u2014 many unrelated changes";
|
|
47761
47752
|
return "Low coherence \u2014 this session touched many unrelated areas";
|
|
47762
47753
|
}
|
|
47763
47754
|
__name(describeCoherence, "describeCoherence");
|
|
@@ -47914,9 +47905,16 @@ var AI_SYNTHESIS_CONFIG;
|
|
|
47914
47905
|
var snapbackEndTool;
|
|
47915
47906
|
var init_snapback_end = __esm2({
|
|
47916
47907
|
"src/tools/v2/snapback-end.ts"() {
|
|
47908
|
+
init_input_sanitization();
|
|
47909
|
+
init_format_duration();
|
|
47917
47910
|
init_utils3();
|
|
47918
47911
|
handleSnapbackEnd = /* @__PURE__ */ __name12(async (args, context) => {
|
|
47919
|
-
const
|
|
47912
|
+
const rawArgs = args;
|
|
47913
|
+
const rawOutcome = typeof rawArgs.outcome === "string" ? rawArgs.outcome : "";
|
|
47914
|
+
const sanitizedOutcome = normalizeContent(rawOutcome).slice(0, MAX_LENGTHS.description);
|
|
47915
|
+
const params = {
|
|
47916
|
+
outcome: sanitizedOutcome
|
|
47917
|
+
};
|
|
47920
47918
|
const workspace = resolveWorkspace(context);
|
|
47921
47919
|
const daemon = getDaemonClient2(workspace);
|
|
47922
47920
|
let currentSession = null;
|
|
@@ -47967,7 +47965,14 @@ var init_snapback_end = __esm2({
|
|
|
47967
47965
|
const payload = buildAIMetadataPayload(ceremonyData, currentSession);
|
|
47968
47966
|
const daemonBaseUrl = process.env.SNAPBACK_API_URL ?? "http://127.0.0.1:4200";
|
|
47969
47967
|
const apiKey = process.env.SNAPBACK_API_KEY;
|
|
47970
|
-
|
|
47968
|
+
const urlHost = new URL(daemonBaseUrl.startsWith("http") ? daemonBaseUrl : `http://${daemonBaseUrl}`).hostname;
|
|
47969
|
+
const isLoopback = urlHost === "127.0.0.1" || urlHost === "localhost" || urlHost === "::1";
|
|
47970
|
+
if (!daemonBaseUrl.startsWith("https://") && !isLoopback) {
|
|
47971
|
+
console.error("[snapback-end] Skipping AI synthesis: Bearer token must not be sent over plain HTTP to remote host");
|
|
47972
|
+
aiSynthesis = null;
|
|
47973
|
+
} else {
|
|
47974
|
+
aiSynthesis = await fetchAISynthesis(payload, currentSession.id ?? currentSession.taskId ?? "unknown", daemonBaseUrl, apiKey);
|
|
47975
|
+
}
|
|
47971
47976
|
} catch {
|
|
47972
47977
|
aiSynthesis = null;
|
|
47973
47978
|
}
|
|
@@ -47980,7 +47985,6 @@ var init_snapback_end = __esm2({
|
|
|
47980
47985
|
});
|
|
47981
47986
|
}, "handleSnapbackEnd");
|
|
47982
47987
|
__name12(formatEndResponse, "formatEndResponse");
|
|
47983
|
-
__name12(formatDuration22, "formatDuration");
|
|
47984
47988
|
__name12(buildSessionDiagnostics, "buildSessionDiagnostics");
|
|
47985
47989
|
__name12(gatherMetrics, "gatherMetrics");
|
|
47986
47990
|
__name12(describeCoherence, "describeCoherence");
|
|
@@ -47998,8 +48002,8 @@ var init_snapback_end = __esm2({
|
|
|
47998
48002
|
__name12(inferScopeType, "inferScopeType");
|
|
47999
48003
|
__name12(fetchAISynthesis, "fetchAISynthesis");
|
|
48000
48004
|
snapbackEndTool = {
|
|
48001
|
-
name: "
|
|
48002
|
-
description: `Close the SnapBack session opened by
|
|
48005
|
+
name: "snap_end",
|
|
48006
|
+
description: `Close the SnapBack session opened by snap_begin and receive a summary
|
|
48003
48007
|
of what was accomplished.
|
|
48004
48008
|
|
|
48005
48009
|
WHAT IT DOES:
|
|
@@ -48012,20 +48016,22 @@ WHAT IT DOES:
|
|
|
48012
48016
|
- Persisted session data is cleaned up
|
|
48013
48017
|
|
|
48014
48018
|
WHEN TO CALL:
|
|
48015
|
-
- At the end of every task, after your work is complete
|
|
48019
|
+
- At the end of every task, after your work is complete and deliverable is ready
|
|
48020
|
+
- Right after generating a final artifact/code \u2014 before your closing message to the user
|
|
48016
48021
|
- When abandoning a task partway through (use outcome to explain why)
|
|
48017
48022
|
- Before starting a completely different task in the same workspace
|
|
48018
48023
|
|
|
48019
48024
|
WHEN NOT TO CALL:
|
|
48020
48025
|
- Mid-task to check progress (use snap_pulse instead)
|
|
48026
|
+
- Still iterating or waiting for user input
|
|
48021
48027
|
- If no session was started (the tool handles this gracefully,
|
|
48022
|
-
but calling
|
|
48028
|
+
but calling snap_begin first gives you a meaningful ceremony)
|
|
48023
48029
|
|
|
48024
48030
|
WORKFLOW POSITION: 4 of 4 (final)
|
|
48025
|
-
|
|
48031
|
+
snap_begin \u2192 [your work] \u2192 snap_pulse (optional) \u2192 snap_end
|
|
48026
48032
|
|
|
48027
|
-
This closes the session opened by
|
|
48028
|
-
should eventually have a matching
|
|
48033
|
+
This closes the session opened by snap_begin. Every snap_begin call
|
|
48034
|
+
should eventually have a matching snap_end.
|
|
48029
48035
|
|
|
48030
48036
|
WHAT YOU GET BACK (natural language text):
|
|
48031
48037
|
- Task name and outcome
|
|
@@ -48041,24 +48047,23 @@ Always safe to call \u2014 gracefully handles missing sessions
|
|
|
48041
48047
|
with a clear message instead of an error.
|
|
48042
48048
|
|
|
48043
48049
|
EXAMPLE:
|
|
48044
|
-
snapback_end({ outcome: "
|
|
48050
|
+
snapback_end({ outcome: "Refactored auth middleware to support OAuth2 PKCE. Token refresh is now in src/auth/refresh.ts. Left incomplete: revoke-token endpoint still uses the old pattern. Decided to keep session cookies HTTP-only after security review." })`,
|
|
48045
48051
|
inputSchema: {
|
|
48046
48052
|
type: "object",
|
|
48047
48053
|
properties: {
|
|
48048
48054
|
outcome: {
|
|
48049
48055
|
type: "string",
|
|
48050
48056
|
title: "Task Outcome",
|
|
48051
|
-
description: `What
|
|
48057
|
+
description: `What you accomplished, what remains incomplete, and any decisions made. This becomes the opening context for your next session \u2014 a vague outcome produces a sparse briefing next time. One paragraph minimum.
|
|
48052
48058
|
|
|
48053
|
-
GOOD: "
|
|
48059
|
+
GOOD: "Refactored auth middleware to support OAuth2 PKCE. Token refresh is now in src/auth/refresh.ts. Left incomplete: revoke-token endpoint still uses the old pattern. Decided to keep session cookies HTTP-only after security review."
|
|
48054
48060
|
GOOD: "Partially done \u2014 auth flow works but token refresh not implemented yet"
|
|
48055
48061
|
GOOD: "Abandoned \u2014 realized this approach won't work, need different strategy"
|
|
48056
48062
|
|
|
48057
48063
|
BAD: "done"
|
|
48064
|
+
BAD: "completed the task"
|
|
48058
48065
|
|
|
48059
|
-
|
|
48060
|
-
Be honest \u2014 "partially done, tests still failing" is more useful
|
|
48061
|
-
than "done" for future session context.`
|
|
48066
|
+
Be honest \u2014 future sessions start from this context.`
|
|
48062
48067
|
}
|
|
48063
48068
|
},
|
|
48064
48069
|
required: [
|
|
@@ -48078,15 +48083,16 @@ than "done" for future session context.`
|
|
|
48078
48083
|
});
|
|
48079
48084
|
function formatLearnResponse(data) {
|
|
48080
48085
|
const sections = [];
|
|
48081
|
-
|
|
48086
|
+
const typeLabel = data.type !== "pattern" ? ` [${data.type}]` : "";
|
|
48087
|
+
sections.push(`Rule encoded${typeLabel} (${data.severity}): when ${data.trigger} \u2192 ${data.action}`);
|
|
48082
48088
|
if (data.implicitSession) {
|
|
48083
|
-
sections.push("\nNote: No active session was found, so one was created automatically. For full codebase intelligence, call `
|
|
48089
|
+
sections.push("\nNote: No active session was found, so one was created automatically. For full codebase intelligence, call `snap_begin` before starting work.");
|
|
48084
48090
|
}
|
|
48085
48091
|
if (data.relatedLearnings.length > 0) {
|
|
48086
48092
|
sections.push(`
|
|
48087
48093
|
## Related Knowledge (${data.relatedLearnings.length} existing)
|
|
48088
48094
|
`);
|
|
48089
|
-
sections.push("Previous
|
|
48095
|
+
sections.push("Previous rules about this area:");
|
|
48090
48096
|
for (const learning of data.relatedLearnings) {
|
|
48091
48097
|
sections.push(`- ${learning}`);
|
|
48092
48098
|
}
|
|
@@ -48187,12 +48193,29 @@ var handleSnapbackLearn;
|
|
|
48187
48193
|
var snapbackLearnTool;
|
|
48188
48194
|
var init_snapback_learn = __esm2({
|
|
48189
48195
|
"src/tools/v2/snapback-learn.ts"() {
|
|
48196
|
+
init_input_sanitization();
|
|
48190
48197
|
init_utils3();
|
|
48191
48198
|
handleSnapbackLearn = /* @__PURE__ */ __name12(async (args, context) => {
|
|
48192
|
-
const
|
|
48199
|
+
const rawArgs = args;
|
|
48200
|
+
const sanitizedArgs = {
|
|
48201
|
+
...rawArgs,
|
|
48202
|
+
...typeof rawArgs.trigger === "string" && {
|
|
48203
|
+
trigger: normalizeContent(rawArgs.trigger).slice(0, MAX_LENGTHS.trigger)
|
|
48204
|
+
},
|
|
48205
|
+
...typeof rawArgs.action === "string" && {
|
|
48206
|
+
action: normalizeContent(rawArgs.action).slice(0, MAX_LENGTHS.action)
|
|
48207
|
+
},
|
|
48208
|
+
...typeof rawArgs.insight === "string" && {
|
|
48209
|
+
insight: normalizeContent(rawArgs.insight).slice(0, MAX_LENGTHS.trigger)
|
|
48210
|
+
}
|
|
48211
|
+
};
|
|
48212
|
+
const params = sanitizedArgs;
|
|
48193
48213
|
const workspace = resolveWorkspace(context);
|
|
48194
48214
|
const daemon = getDaemonClient2(workspace);
|
|
48195
48215
|
const severity = params.severity ?? "info";
|
|
48216
|
+
const trigger = params.trigger ?? params.insight ?? "";
|
|
48217
|
+
const action = params.action ?? deriveAction(params.insight ?? "", severity);
|
|
48218
|
+
const type = params.type ?? mapSeverityToType(severity);
|
|
48196
48219
|
let implicitSession = false;
|
|
48197
48220
|
let currentSessionId;
|
|
48198
48221
|
try {
|
|
@@ -48200,11 +48223,12 @@ var init_snapback_learn = __esm2({
|
|
|
48200
48223
|
workspace
|
|
48201
48224
|
});
|
|
48202
48225
|
if (!isActiveSession(currentSession)) {
|
|
48203
|
-
await daemon.request("session/begin", {
|
|
48226
|
+
const beginResult = await daemon.request("session/begin", {
|
|
48204
48227
|
workspace,
|
|
48205
48228
|
task: "Implicit session (learning captured before explicit session start)",
|
|
48206
48229
|
files: params.files ?? []
|
|
48207
48230
|
});
|
|
48231
|
+
currentSessionId = beginResult?.sessionId ?? beginResult?.taskId;
|
|
48208
48232
|
implicitSession = true;
|
|
48209
48233
|
} else {
|
|
48210
48234
|
currentSessionId = currentSession.id ?? currentSession.taskId;
|
|
@@ -48215,13 +48239,13 @@ var init_snapback_learn = __esm2({
|
|
|
48215
48239
|
}
|
|
48216
48240
|
}
|
|
48217
48241
|
const learningPayload = {
|
|
48218
|
-
type
|
|
48219
|
-
trigger
|
|
48220
|
-
solution:
|
|
48221
|
-
action
|
|
48242
|
+
type,
|
|
48243
|
+
trigger,
|
|
48244
|
+
solution: trigger,
|
|
48245
|
+
action,
|
|
48222
48246
|
source: "llm-observation",
|
|
48223
48247
|
priority: mapSeverityToPriority(severity),
|
|
48224
|
-
keywords: extractKeywords22(
|
|
48248
|
+
keywords: extractKeywords22(`${trigger} ${action}`),
|
|
48225
48249
|
related: params.files
|
|
48226
48250
|
};
|
|
48227
48251
|
let addResult = null;
|
|
@@ -48243,7 +48267,7 @@ var init_snapback_learn = __esm2({
|
|
|
48243
48267
|
try {
|
|
48244
48268
|
const searchResult = await daemon.request("learning/search", {
|
|
48245
48269
|
workspace,
|
|
48246
|
-
query:
|
|
48270
|
+
query: trigger,
|
|
48247
48271
|
files: params.files,
|
|
48248
48272
|
limit: 5
|
|
48249
48273
|
});
|
|
@@ -48257,7 +48281,9 @@ var init_snapback_learn = __esm2({
|
|
|
48257
48281
|
implicitSession,
|
|
48258
48282
|
relatedLearnings,
|
|
48259
48283
|
severity,
|
|
48260
|
-
|
|
48284
|
+
type,
|
|
48285
|
+
trigger,
|
|
48286
|
+
action
|
|
48261
48287
|
});
|
|
48262
48288
|
}, "handleSnapbackLearn");
|
|
48263
48289
|
__name12(formatLearnResponse, "formatLearnResponse");
|
|
@@ -48266,60 +48292,62 @@ var init_snapback_learn = __esm2({
|
|
|
48266
48292
|
__name12(deriveAction, "deriveAction");
|
|
48267
48293
|
__name12(extractKeywords22, "extractKeywords");
|
|
48268
48294
|
snapbackLearnTool = {
|
|
48269
|
-
name: "
|
|
48270
|
-
description: `
|
|
48295
|
+
name: "snap_learn",
|
|
48296
|
+
description: `Encode a reusable IF/THEN rule into the workspace knowledge base. trigger: the condition under which this rule applies ('when editing auth.ts without running tests'). action: what to do when that condition is true ('always snapshot before touching token refresh logic'). Not for observations or file annotations \u2014 call snap_pulse with a query param for retrieval.
|
|
48271
48297
|
|
|
48272
|
-
|
|
48273
|
-
-
|
|
48274
|
-
|
|
48275
|
-
-
|
|
48276
|
-
-
|
|
48277
|
-
|
|
48278
|
-
|
|
48279
|
-
WHEN TO CALL:
|
|
48280
|
-
- When you discover a pattern: "These two files must always change together"
|
|
48281
|
-
- When you hit a pitfall: "Editing this config without rebuilding causes silent failures"
|
|
48282
|
-
- When you learn a constraint: "This module cannot import from the platform package"
|
|
48283
|
-
- When you identify a preference: "Team prefers explicit error handling over try-catch"
|
|
48298
|
+
CALL IMMEDIATELY WHEN:
|
|
48299
|
+
- User corrects an assumption you made ("Actually, we use X not Y")
|
|
48300
|
+
- You discover a codebase pattern that contradicts your expectations
|
|
48301
|
+
- An error reveals an unknown constraint or fragile dependency
|
|
48302
|
+
- You find undocumented behavior that future sessions must know about
|
|
48303
|
+
- You identify a team preference or hard rule
|
|
48284
48304
|
|
|
48285
48305
|
WHEN NOT TO CALL:
|
|
48286
|
-
- To
|
|
48287
|
-
-
|
|
48288
|
-
- To
|
|
48289
|
-
- For trivial observations that won't help future sessions
|
|
48306
|
+
- To retrieve learnings \u2192 snap_pulse with query param (read-only)
|
|
48307
|
+
- For trivial observations that won't matter in future sessions
|
|
48308
|
+
- To confirm expected/standard behavior
|
|
48290
48309
|
|
|
48291
|
-
WORKFLOW POSITION:
|
|
48292
|
-
|
|
48310
|
+
WORKFLOW POSITION: Anytime during a session.
|
|
48311
|
+
snap_begin \u2192 [work] \u2192 snap_learn \u2192 [work] \u2192 snap_learn \u2192 snap_end
|
|
48293
48312
|
|
|
48294
48313
|
Works without an active session, but for full intelligence context,
|
|
48295
|
-
call
|
|
48314
|
+
call snap_begin first. Without a session, learnings are stored but
|
|
48296
48315
|
not attributed to any task in the closing ceremony.
|
|
48297
48316
|
|
|
48298
48317
|
WHAT YOU GET BACK (natural language text):
|
|
48299
|
-
- Confirmation
|
|
48300
|
-
-
|
|
48301
|
-
- Similar learnings already in the knowledge base (to avoid duplicates
|
|
48302
|
-
and reinforce existing knowledge)
|
|
48318
|
+
- Confirmation the rule was stored
|
|
48319
|
+
- Related learnings already in the knowledge base (deduplication loop)
|
|
48303
48320
|
|
|
48304
48321
|
EXAMPLE:
|
|
48305
|
-
|
|
48322
|
+
snap_learn({
|
|
48323
|
+
trigger: "when modifying token refresh logic in auth.ts",
|
|
48324
|
+
action: "snapshot first \u2014 this code has 3 rollbacks across 2 sessions",
|
|
48325
|
+
type: "pitfall",
|
|
48326
|
+
severity: "warning",
|
|
48327
|
+
files: ["src/auth/auth.ts"]
|
|
48328
|
+
})`,
|
|
48306
48329
|
inputSchema: {
|
|
48307
48330
|
type: "object",
|
|
48308
48331
|
properties: {
|
|
48309
|
-
|
|
48332
|
+
trigger: {
|
|
48310
48333
|
type: "string",
|
|
48311
|
-
title: "
|
|
48312
|
-
description: `The
|
|
48313
|
-
|
|
48314
|
-
GOOD: "The session-registry.ts appendTouchedFile method silently
|
|
48315
|
-
deduplicates \u2014 calling it twice with the same path is safe
|
|
48316
|
-
and expected during burst file changes."
|
|
48334
|
+
title: "Condition",
|
|
48335
|
+
description: `The condition. Starts with 'when' \u2014 when X is true, when Y happens, when you're in Z state.
|
|
48317
48336
|
|
|
48318
|
-
|
|
48319
|
-
|
|
48337
|
+
GOOD: "when editing auth.ts without running tests first"
|
|
48338
|
+
GOOD: "when the daemon restarts during an active session"
|
|
48339
|
+
BAD: "auth.ts is fragile"
|
|
48340
|
+
BAD: "I noticed something"`
|
|
48341
|
+
},
|
|
48342
|
+
action: {
|
|
48343
|
+
type: "string",
|
|
48344
|
+
title: "Action",
|
|
48345
|
+
description: `What to do when the trigger fires. Starts with a verb.
|
|
48320
48346
|
|
|
48321
|
-
|
|
48322
|
-
|
|
48347
|
+
GOOD: "always snapshot before touching token refresh logic"
|
|
48348
|
+
GOOD: "check that session.json was cleaned up before starting a new session"
|
|
48349
|
+
BAD: "be careful"
|
|
48350
|
+
BAD: "token refresh is tricky"`
|
|
48323
48351
|
},
|
|
48324
48352
|
files: {
|
|
48325
48353
|
type: "array",
|
|
@@ -48327,10 +48355,25 @@ learnings by keyword relevance \u2014 specific terms help retrieval.`
|
|
|
48327
48355
|
type: "string"
|
|
48328
48356
|
},
|
|
48329
48357
|
title: "Related Files",
|
|
48330
|
-
description: `Files this
|
|
48331
|
-
those files are being modified.
|
|
48358
|
+
description: `Files this rule is scoped to. Narrows retrieval relevance.
|
|
48332
48359
|
|
|
48333
48360
|
Example: ["src/services/session-registry.ts"]`
|
|
48361
|
+
},
|
|
48362
|
+
type: {
|
|
48363
|
+
type: "string",
|
|
48364
|
+
enum: [
|
|
48365
|
+
"pattern",
|
|
48366
|
+
"pitfall",
|
|
48367
|
+
"constraint",
|
|
48368
|
+
"preference"
|
|
48369
|
+
],
|
|
48370
|
+
title: "Rule Type",
|
|
48371
|
+
description: `Controls how this learning surfaces in future briefings.
|
|
48372
|
+
|
|
48373
|
+
"pattern" \u2014 A repeating structure worth following
|
|
48374
|
+
"pitfall" \u2014 A known failure mode to avoid
|
|
48375
|
+
"constraint" \u2014 A hard rule that must not be violated
|
|
48376
|
+
"preference" \u2014 A soft team preference`
|
|
48334
48377
|
},
|
|
48335
48378
|
severity: {
|
|
48336
48379
|
type: "string",
|
|
@@ -48340,15 +48383,21 @@ Example: ["src/services/session-registry.ts"]`
|
|
|
48340
48383
|
"critical"
|
|
48341
48384
|
],
|
|
48342
48385
|
title: "Severity Level",
|
|
48343
|
-
description: `How
|
|
48386
|
+
description: `How urgently this rule surfaces.
|
|
48344
48387
|
|
|
48345
|
-
"info" \u2014
|
|
48346
|
-
"warning" \u2014
|
|
48347
|
-
"critical" \u2014
|
|
48388
|
+
"info" \u2014 Shown when relevant files are in scope (default)
|
|
48389
|
+
"warning" \u2014 Shown whenever related files are modified
|
|
48390
|
+
"critical" \u2014 Shown in snap_begin even on cold start, regardless of file scope`
|
|
48391
|
+
},
|
|
48392
|
+
insight: {
|
|
48393
|
+
type: "string",
|
|
48394
|
+
title: "Insight (deprecated)",
|
|
48395
|
+
description: "Deprecated. Use trigger + action instead."
|
|
48348
48396
|
}
|
|
48349
48397
|
},
|
|
48350
48398
|
required: [
|
|
48351
|
-
"
|
|
48399
|
+
"trigger",
|
|
48400
|
+
"action"
|
|
48352
48401
|
]
|
|
48353
48402
|
},
|
|
48354
48403
|
annotations: {
|
|
@@ -48516,10 +48565,9 @@ function formatHealthyResponse(session, vitals) {
|
|
|
48516
48565
|
} else if (session.filesTouched) {
|
|
48517
48566
|
parts.push(`${session.filesTouched} files touched`);
|
|
48518
48567
|
}
|
|
48519
|
-
|
|
48520
|
-
|
|
48521
|
-
|
|
48522
|
-
parts.push(`${session.snapshotsCreated} snapshots`);
|
|
48568
|
+
const healthySnapshots = vitals?.snapshotsCreated ?? session.snapshotsCreated;
|
|
48569
|
+
if (healthySnapshots) {
|
|
48570
|
+
parts.push(`${healthySnapshots} snapshots`);
|
|
48523
48571
|
}
|
|
48524
48572
|
if (vitals) {
|
|
48525
48573
|
parts.push(`Pulse: ${vitals.pulse}`);
|
|
@@ -48623,12 +48671,12 @@ function formatGuardHealth(health, touchedFiles) {
|
|
|
48623
48671
|
lines.push(` Issues: ${failCount} failing, ${warnCount} warning${warnCount !== 1 ? "s" : ""}`);
|
|
48624
48672
|
}
|
|
48625
48673
|
const relevantIssues = [];
|
|
48626
|
-
const touchedSet = touchedFiles ? new Set(touchedFiles.map((f) => f.
|
|
48674
|
+
const touchedSet = touchedFiles ? new Set(touchedFiles.map((f) => f.replace(/\\/g, "/").replace(/\/+$/, ""))) : /* @__PURE__ */ new Set();
|
|
48627
48675
|
for (const guard2 of health.guards) {
|
|
48628
48676
|
if (guard2.status === "pass") continue;
|
|
48629
48677
|
for (const file of guard2.files) {
|
|
48630
|
-
const
|
|
48631
|
-
if (touchedSet.size === 0 || touchedSet.has(
|
|
48678
|
+
const normalizedFilePath = file.path.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
48679
|
+
if (touchedSet.size === 0 || touchedSet.has(normalizedFilePath) || relevantIssues.length < 3) {
|
|
48632
48680
|
relevantIssues.push({
|
|
48633
48681
|
guard: guard2.guard,
|
|
48634
48682
|
status: guard2.status,
|
|
@@ -48761,6 +48809,7 @@ var handleSnapbackPulse;
|
|
|
48761
48809
|
var snapbackPulseTool;
|
|
48762
48810
|
var init_snapback_pulse = __esm2({
|
|
48763
48811
|
"src/tools/v2/snapback-pulse.ts"() {
|
|
48812
|
+
init_input_sanitization();
|
|
48764
48813
|
init_utils3();
|
|
48765
48814
|
RISK_THRESHOLD = 0.3;
|
|
48766
48815
|
CO_CHANGE_CONFIDENCE_THRESHOLD = 0.5;
|
|
@@ -48773,7 +48822,9 @@ var init_snapback_pulse = __esm2({
|
|
|
48773
48822
|
const params = args ?? {};
|
|
48774
48823
|
const workspace = resolveWorkspace(context);
|
|
48775
48824
|
const daemon = getDaemonClient2(workspace);
|
|
48776
|
-
const
|
|
48825
|
+
const rawTask = params.task ? normalizeContent(params.task).slice(0, MAX_LENGTHS.task) : void 0;
|
|
48826
|
+
const rawQuery = params.query ? normalizeContent(params.query).slice(0, MAX_LENGTHS.task) : void 0;
|
|
48827
|
+
const effectiveQuery = rawTask ?? rawQuery;
|
|
48777
48828
|
const [sessionResult, vitalsResult, warningsResult, fragileResult, coChangeResult, guardHealthResult, topologyStatusResult, topologyQueryResult, learningSearchResult] = await Promise.allSettled([
|
|
48778
48829
|
daemon.request("session/current", {
|
|
48779
48830
|
workspace
|
|
@@ -48876,8 +48927,7 @@ var init_snapback_pulse = __esm2({
|
|
|
48876
48927
|
__name12(countMissingCoChanges, "countMissingCoChanges");
|
|
48877
48928
|
snapbackPulseTool = {
|
|
48878
48929
|
name: "snap_pulse",
|
|
48879
|
-
description: `
|
|
48880
|
-
Read-only \u2014 does not modify any state or create snapshots.
|
|
48930
|
+
description: `Read-only session health check. Does not write, learn, or create snapshots.
|
|
48881
48931
|
|
|
48882
48932
|
WHAT IT DOES:
|
|
48883
48933
|
- Returns current session vitals: pulse (activity level), pressure
|
|
@@ -48891,18 +48941,21 @@ WHAT IT DOES:
|
|
|
48891
48941
|
|
|
48892
48942
|
WHEN TO CALL:
|
|
48893
48943
|
- After modifying 3+ files, to check if pressure is building
|
|
48944
|
+
- After 3+ tool calls on the same task without a checkpoint
|
|
48894
48945
|
- Before a destructive change (delete, revert, major refactor)
|
|
48946
|
+
- When multiple valid implementation paths exist \u2014 before committing to one
|
|
48947
|
+
- Before asking the user a clarifying question (capture state first)
|
|
48895
48948
|
- When switching context between different areas of the codebase
|
|
48896
48949
|
- To verify the session is still active after an interruption
|
|
48897
48950
|
- When starting work on a task \u2014 provide task description for oriented briefing
|
|
48898
48951
|
|
|
48899
48952
|
WHEN NOT TO CALL:
|
|
48900
|
-
- To start a session (use
|
|
48901
|
-
- To end a session (use
|
|
48902
|
-
- Immediately after
|
|
48953
|
+
- To start a session (use snap_begin instead)
|
|
48954
|
+
- To end a session (use snap_end instead)
|
|
48955
|
+
- Immediately after snap_begin \u2014 the session just started, there's nothing to check yet
|
|
48903
48956
|
|
|
48904
48957
|
WORKFLOW POSITION: 3 of 4 (optional, repeatable)
|
|
48905
|
-
|
|
48958
|
+
snap_begin \u2192 [your work] \u2192 snap_pulse \u2192 [more work] \u2192 snap_pulse \u2192 snap_end
|
|
48906
48959
|
|
|
48907
48960
|
WHAT YOU GET BACK (natural language text):
|
|
48908
48961
|
- Session duration and file count
|
|
@@ -48956,8 +49009,8 @@ Example: "add rate limiting to API endpoints"`
|
|
|
48956
49009
|
query: {
|
|
48957
49010
|
type: "string",
|
|
48958
49011
|
title: "Query",
|
|
48959
|
-
description: `
|
|
48960
|
-
|
|
49012
|
+
description: `Retrieve relevant learnings from the knowledge base. Read-only \u2014 does not store anything. To write a new learning, use snap_learn instead.
|
|
49013
|
+
|
|
48961
49014
|
Results are returned from BM25 search over captured learnings.
|
|
48962
49015
|
|
|
48963
49016
|
Example: "What should I know before changing the auth middleware?"
|
|
@@ -48992,10 +49045,15 @@ var init_v2 = __esm2({
|
|
|
48992
49045
|
snapbackPulseTool
|
|
48993
49046
|
];
|
|
48994
49047
|
V2_HANDLERS = {
|
|
49048
|
+
// Legacy keys (internal routing, backward compat)
|
|
48995
49049
|
snapback: handleSnapback,
|
|
48996
49050
|
snapback_learn: handleSnapbackLearn,
|
|
48997
49051
|
snapback_end: handleSnapbackEnd,
|
|
48998
|
-
snap_pulse: handleSnapbackPulse
|
|
49052
|
+
snap_pulse: handleSnapbackPulse,
|
|
49053
|
+
// Canonical keys — must match SnapBackTool.name for ToolRegistry dispatch
|
|
49054
|
+
snap_begin: handleSnapback,
|
|
49055
|
+
snap_learn: handleSnapbackLearn,
|
|
49056
|
+
snap_end: handleSnapbackEnd
|
|
48999
49057
|
};
|
|
49000
49058
|
}
|
|
49001
49059
|
});
|
|
@@ -50932,7 +50990,7 @@ var init_evolution_detector_service = __esm2({
|
|
|
50932
50990
|
}
|
|
50933
50991
|
});
|
|
50934
50992
|
async function getGitDiffFiles2(workspacePath, scope = "staged") {
|
|
50935
|
-
return new Promise((
|
|
50993
|
+
return new Promise((resolve62) => {
|
|
50936
50994
|
let args;
|
|
50937
50995
|
switch (scope) {
|
|
50938
50996
|
case "staged":
|
|
@@ -50978,14 +51036,14 @@ async function getGitDiffFiles2(workspacePath, scope = "staged") {
|
|
|
50978
51036
|
});
|
|
50979
51037
|
proc.on("close", (code) => {
|
|
50980
51038
|
if (code !== 0) {
|
|
50981
|
-
|
|
51039
|
+
resolve62([]);
|
|
50982
51040
|
return;
|
|
50983
51041
|
}
|
|
50984
51042
|
const files = stdout.split("\n").map((f) => f.trim()).filter((f) => f.length > 0).filter((f) => /\.(ts|tsx|js|jsx)$/.test(f));
|
|
50985
|
-
|
|
51043
|
+
resolve62(files);
|
|
50986
51044
|
});
|
|
50987
51045
|
proc.on("error", () => {
|
|
50988
|
-
|
|
51046
|
+
resolve62([]);
|
|
50989
51047
|
});
|
|
50990
51048
|
});
|
|
50991
51049
|
}
|
|
@@ -51019,7 +51077,7 @@ var init_file_selector = __esm2({
|
|
|
51019
51077
|
});
|
|
51020
51078
|
function runCommand22(cmd, args, cwd, timeoutMs = 3e4) {
|
|
51021
51079
|
const start = Date.now();
|
|
51022
|
-
return new Promise((
|
|
51080
|
+
return new Promise((resolve62) => {
|
|
51023
51081
|
let stdout = "";
|
|
51024
51082
|
let stderr = "";
|
|
51025
51083
|
let killed = false;
|
|
@@ -51044,7 +51102,7 @@ function runCommand22(cmd, args, cwd, timeoutMs = 3e4) {
|
|
|
51044
51102
|
});
|
|
51045
51103
|
proc.on("close", (code) => {
|
|
51046
51104
|
clearTimeout(timeout);
|
|
51047
|
-
|
|
51105
|
+
resolve62({
|
|
51048
51106
|
exitCode: killed ? -1 : code ?? 0,
|
|
51049
51107
|
stdout,
|
|
51050
51108
|
stderr,
|
|
@@ -51053,7 +51111,7 @@ function runCommand22(cmd, args, cwd, timeoutMs = 3e4) {
|
|
|
51053
51111
|
});
|
|
51054
51112
|
proc.on("error", (err22) => {
|
|
51055
51113
|
clearTimeout(timeout);
|
|
51056
|
-
|
|
51114
|
+
resolve62({
|
|
51057
51115
|
exitCode: -1,
|
|
51058
51116
|
stdout,
|
|
51059
51117
|
stderr: err22.message,
|
|
@@ -51835,7 +51893,7 @@ var init_validation2 = __esm2({
|
|
|
51835
51893
|
}
|
|
51836
51894
|
}
|
|
51837
51895
|
async runMadgeCircular(workspacePath) {
|
|
51838
|
-
return new Promise((
|
|
51896
|
+
return new Promise((resolve62) => {
|
|
51839
51897
|
const madgePath = join(workspacePath, "node_modules", ".bin", "madge");
|
|
51840
51898
|
const hasLocalMadge = existsSync(madgePath);
|
|
51841
51899
|
const args = [
|
|
@@ -51864,16 +51922,16 @@ var init_validation2 = __esm2({
|
|
|
51864
51922
|
try {
|
|
51865
51923
|
const result7 = JSON.parse(stdout);
|
|
51866
51924
|
if (Array.isArray(result7)) {
|
|
51867
|
-
|
|
51925
|
+
resolve62(result7);
|
|
51868
51926
|
} else {
|
|
51869
|
-
|
|
51927
|
+
resolve62([]);
|
|
51870
51928
|
}
|
|
51871
51929
|
} catch {
|
|
51872
|
-
|
|
51930
|
+
resolve62([]);
|
|
51873
51931
|
}
|
|
51874
51932
|
});
|
|
51875
51933
|
proc.on("error", () => {
|
|
51876
|
-
|
|
51934
|
+
resolve62([]);
|
|
51877
51935
|
});
|
|
51878
51936
|
});
|
|
51879
51937
|
}
|
|
@@ -51940,7 +51998,7 @@ var init_validation2 = __esm2({
|
|
|
51940
51998
|
};
|
|
51941
51999
|
}
|
|
51942
52000
|
async checkToolAvailability(tool, args) {
|
|
51943
|
-
return new Promise((
|
|
52001
|
+
return new Promise((resolve62) => {
|
|
51944
52002
|
const startTime = Date.now();
|
|
51945
52003
|
const proc = spawn("npx", [
|
|
51946
52004
|
tool,
|
|
@@ -51954,13 +52012,13 @@ var init_validation2 = __esm2({
|
|
|
51954
52012
|
timeout: 5e3
|
|
51955
52013
|
});
|
|
51956
52014
|
proc.on("close", (code) => {
|
|
51957
|
-
|
|
52015
|
+
resolve62({
|
|
51958
52016
|
healthy: code === 0,
|
|
51959
52017
|
latency: Date.now() - startTime
|
|
51960
52018
|
});
|
|
51961
52019
|
});
|
|
51962
52020
|
proc.on("error", () => {
|
|
51963
|
-
|
|
52021
|
+
resolve62({
|
|
51964
52022
|
healthy: false,
|
|
51965
52023
|
latency: Date.now() - startTime
|
|
51966
52024
|
});
|
|
@@ -51997,7 +52055,7 @@ var init_validation2 = __esm2({
|
|
|
51997
52055
|
}
|
|
51998
52056
|
}
|
|
51999
52057
|
async runMadgeOrphans(workspacePath) {
|
|
52000
|
-
return new Promise((
|
|
52058
|
+
return new Promise((resolve62) => {
|
|
52001
52059
|
const madgePath = join(workspacePath, "node_modules", ".bin", "madge");
|
|
52002
52060
|
const hasLocalMadge = existsSync(madgePath);
|
|
52003
52061
|
const args = [
|
|
@@ -52026,16 +52084,16 @@ var init_validation2 = __esm2({
|
|
|
52026
52084
|
try {
|
|
52027
52085
|
const result7 = JSON.parse(stdout);
|
|
52028
52086
|
if (Array.isArray(result7)) {
|
|
52029
|
-
|
|
52087
|
+
resolve62(result7);
|
|
52030
52088
|
} else {
|
|
52031
|
-
|
|
52089
|
+
resolve62([]);
|
|
52032
52090
|
}
|
|
52033
52091
|
} catch {
|
|
52034
|
-
|
|
52092
|
+
resolve62([]);
|
|
52035
52093
|
}
|
|
52036
52094
|
});
|
|
52037
52095
|
proc.on("error", () => {
|
|
52038
|
-
|
|
52096
|
+
resolve62([]);
|
|
52039
52097
|
});
|
|
52040
52098
|
});
|
|
52041
52099
|
}
|
|
@@ -52095,7 +52153,7 @@ var init_validation2 = __esm2({
|
|
|
52095
52153
|
return diagnostics;
|
|
52096
52154
|
}
|
|
52097
52155
|
async runTypeScriptCheck(workspacePath, _files) {
|
|
52098
|
-
return new Promise((
|
|
52156
|
+
return new Promise((resolve62) => {
|
|
52099
52157
|
const diagnostics = [];
|
|
52100
52158
|
const tscPath = join(workspacePath, "node_modules", ".bin", "tsc");
|
|
52101
52159
|
const hasLocalTsc = existsSync(tscPath);
|
|
@@ -52138,19 +52196,19 @@ var init_validation2 = __esm2({
|
|
|
52138
52196
|
});
|
|
52139
52197
|
}
|
|
52140
52198
|
}
|
|
52141
|
-
|
|
52199
|
+
resolve62({
|
|
52142
52200
|
diagnostics
|
|
52143
52201
|
});
|
|
52144
52202
|
});
|
|
52145
52203
|
proc.on("error", () => {
|
|
52146
|
-
|
|
52204
|
+
resolve62({
|
|
52147
52205
|
diagnostics: []
|
|
52148
52206
|
});
|
|
52149
52207
|
});
|
|
52150
52208
|
});
|
|
52151
52209
|
}
|
|
52152
52210
|
async runLintCheck(workspacePath, files, fix = false) {
|
|
52153
|
-
return new Promise((
|
|
52211
|
+
return new Promise((resolve62) => {
|
|
52154
52212
|
const diagnostics = [];
|
|
52155
52213
|
const biomePath = join(workspacePath, "node_modules", ".bin", "biome");
|
|
52156
52214
|
const hasLocalBiome = existsSync(biomePath);
|
|
@@ -52192,19 +52250,19 @@ var init_validation2 = __esm2({
|
|
|
52192
52250
|
});
|
|
52193
52251
|
}
|
|
52194
52252
|
}
|
|
52195
|
-
|
|
52253
|
+
resolve62({
|
|
52196
52254
|
diagnostics
|
|
52197
52255
|
});
|
|
52198
52256
|
});
|
|
52199
52257
|
proc.on("error", () => {
|
|
52200
|
-
|
|
52258
|
+
resolve62({
|
|
52201
52259
|
diagnostics: []
|
|
52202
52260
|
});
|
|
52203
52261
|
});
|
|
52204
52262
|
});
|
|
52205
52263
|
}
|
|
52206
52264
|
async runBuildCommand(workspacePath) {
|
|
52207
|
-
return new Promise((
|
|
52265
|
+
return new Promise((resolve62) => {
|
|
52208
52266
|
const proc = spawn("npm", [
|
|
52209
52267
|
"run",
|
|
52210
52268
|
"build"
|
|
@@ -52234,13 +52292,13 @@ var init_validation2 = __esm2({
|
|
|
52234
52292
|
}
|
|
52235
52293
|
}
|
|
52236
52294
|
}
|
|
52237
|
-
|
|
52295
|
+
resolve62({
|
|
52238
52296
|
success: code === 0,
|
|
52239
52297
|
diagnostics
|
|
52240
52298
|
});
|
|
52241
52299
|
});
|
|
52242
52300
|
proc.on("error", () => {
|
|
52243
|
-
|
|
52301
|
+
resolve62({
|
|
52244
52302
|
success: false,
|
|
52245
52303
|
diagnostics: []
|
|
52246
52304
|
});
|
|
@@ -52550,7 +52608,7 @@ __export2(check_exports, {
|
|
|
52550
52608
|
async function detectGitConflicts(workspaceRoot) {
|
|
52551
52609
|
const { spawn: spawn62 } = await import('child_process');
|
|
52552
52610
|
const { access: access22 } = await import('fs/promises');
|
|
52553
|
-
const conflictedFiles = await new Promise((
|
|
52611
|
+
const conflictedFiles = await new Promise((resolve62) => {
|
|
52554
52612
|
const proc = spawn62("git", [
|
|
52555
52613
|
"diff",
|
|
52556
52614
|
"--name-only",
|
|
@@ -52569,9 +52627,9 @@ async function detectGitConflicts(workspaceRoot) {
|
|
|
52569
52627
|
});
|
|
52570
52628
|
proc.on("close", () => {
|
|
52571
52629
|
const files = stdout.split("\n").map((f) => f.trim()).filter((f) => f.length > 0);
|
|
52572
|
-
|
|
52630
|
+
resolve62(files);
|
|
52573
52631
|
});
|
|
52574
|
-
proc.on("error", () =>
|
|
52632
|
+
proc.on("error", () => resolve62([]));
|
|
52575
52633
|
});
|
|
52576
52634
|
let source = "unknown";
|
|
52577
52635
|
try {
|
|
@@ -52601,7 +52659,7 @@ async function detectGitConflicts(workspaceRoot) {
|
|
|
52601
52659
|
__name(detectGitConflicts, "detectGitConflicts");
|
|
52602
52660
|
async function getGitDiffFiles22(workspaceRoot, scope = "staged") {
|
|
52603
52661
|
const { spawn: spawn62 } = await import('child_process');
|
|
52604
|
-
return new Promise((
|
|
52662
|
+
return new Promise((resolve62) => {
|
|
52605
52663
|
let args;
|
|
52606
52664
|
switch (scope) {
|
|
52607
52665
|
case "staged":
|
|
@@ -52647,13 +52705,13 @@ async function getGitDiffFiles22(workspaceRoot, scope = "staged") {
|
|
|
52647
52705
|
});
|
|
52648
52706
|
proc.on("close", (code) => {
|
|
52649
52707
|
if (code !== 0) {
|
|
52650
|
-
|
|
52708
|
+
resolve62([]);
|
|
52651
52709
|
return;
|
|
52652
52710
|
}
|
|
52653
52711
|
const files = stdout.split("\n").map((f) => f.trim()).filter((f) => f.length > 0).filter((f) => /\.(ts|tsx|js|jsx)$/.test(f));
|
|
52654
|
-
|
|
52712
|
+
resolve62(files);
|
|
52655
52713
|
});
|
|
52656
|
-
proc.on("error", () =>
|
|
52714
|
+
proc.on("error", () => resolve62([]));
|
|
52657
52715
|
});
|
|
52658
52716
|
}
|
|
52659
52717
|
__name(getGitDiffFiles22, "getGitDiffFiles2");
|
|
@@ -56627,6 +56685,9 @@ function buildWireFields(response) {
|
|
|
56627
56685
|
fields.next = actionsStr;
|
|
56628
56686
|
}
|
|
56629
56687
|
}
|
|
56688
|
+
if (response.open_session_reminder) {
|
|
56689
|
+
fields.reminder = response.open_session_reminder;
|
|
56690
|
+
}
|
|
56630
56691
|
return fields;
|
|
56631
56692
|
}
|
|
56632
56693
|
__name(buildWireFields, "buildWireFields");
|
|
@@ -56671,6 +56732,9 @@ function formatWireCompact(response) {
|
|
|
56671
56732
|
break;
|
|
56672
56733
|
}
|
|
56673
56734
|
}
|
|
56735
|
+
if (response.open_session_reminder) {
|
|
56736
|
+
parts.push(`reminder:${compress2(response.open_session_reminder, 40)}`);
|
|
56737
|
+
}
|
|
56674
56738
|
return parts.join("|");
|
|
56675
56739
|
}
|
|
56676
56740
|
__name(formatWireCompact, "formatWireCompact");
|
|
@@ -56975,9 +57039,10 @@ async function handleStart(normalized, context) {
|
|
|
56975
57039
|
} else {
|
|
56976
57040
|
return result7;
|
|
56977
57041
|
}
|
|
56978
|
-
const snapshotStatus = data.snapshot?.created ? "created" : data.snapshot?.id ? "reused" : "skipped";
|
|
57042
|
+
const snapshotStatus = data.snapshot?.created ? "created" : data.snapshot?.id || data.snapshotIds && data.snapshotIds.length > 0 ? "reused" : "skipped";
|
|
56979
57043
|
const integrationEnrichment = await getIntegrationEnrichment(normalized.files || [], context);
|
|
56980
|
-
const
|
|
57044
|
+
const rawRisk = data.risk?.[0] || data.riskAssessment?.overallRisk || "l";
|
|
57045
|
+
const riskLevel = rawRisk[0].toUpperCase();
|
|
56981
57046
|
const warningCount = data.hotspots?.reduce((sum, h) => sum + h.violations, 0) || 0;
|
|
56982
57047
|
const nextActions = generateNextActions2({
|
|
56983
57048
|
risk: riskLevel,
|
|
@@ -57627,7 +57692,7 @@ function identifyImprovements(data, params) {
|
|
|
57627
57692
|
priority: "medium"
|
|
57628
57693
|
});
|
|
57629
57694
|
}
|
|
57630
|
-
if (params.ok === 0
|
|
57695
|
+
if (params.ok === 0) {
|
|
57631
57696
|
improvements.push({
|
|
57632
57697
|
suggestion: "Capture specific blocker as pitfall learning",
|
|
57633
57698
|
trigger: "Task did not complete successfully",
|
|
@@ -57802,6 +57867,18 @@ var init_session_reporter = __esm2({
|
|
|
57802
57867
|
__name12(suggestNextSteps, "suggestNextSteps");
|
|
57803
57868
|
}
|
|
57804
57869
|
});
|
|
57870
|
+
function normalizeOutcome(outcome, ok22) {
|
|
57871
|
+
if (!outcome) return ok22 === 0 || ok22 === false ? "abandoned" : "completed";
|
|
57872
|
+
const lower = outcome.toLowerCase().trim();
|
|
57873
|
+
if (lower === "completed" || lower === "success") return "completed";
|
|
57874
|
+
if (lower === "abandoned" || lower === "cancelled" || lower === "canceled") return "abandoned";
|
|
57875
|
+
if (lower === "blocked" || lower === "failed" || lower === "failure" || lower === "error") return "blocked";
|
|
57876
|
+
if (/\bcomplet|\bfinish|\bdone|\bsuccess/.test(lower)) return "completed";
|
|
57877
|
+
if (/\babandon|\bcancel|\bgave up|\bgive up|\bscrapped/.test(lower)) return "abandoned";
|
|
57878
|
+
if (/\bblock|\bfail|\bstuck|\bbroken|\berror/.test(lower)) return "blocked";
|
|
57879
|
+
return ok22 === 0 || ok22 === false ? "abandoned" : "completed";
|
|
57880
|
+
}
|
|
57881
|
+
__name(normalizeOutcome, "normalizeOutcome");
|
|
57805
57882
|
function formatUserStats(metrics) {
|
|
57806
57883
|
const parts = [];
|
|
57807
57884
|
if (metrics.saved !== "not reported") parts.push(`\u{1F4B0} ${metrics.saved} tokens saved`);
|
|
@@ -57832,6 +57909,7 @@ var init_snap_end = __esm2({
|
|
|
57832
57909
|
const clean = s.replace(/\s+/g, "\u2192");
|
|
57833
57910
|
return clean.length > max ? `${clean.slice(0, max - 1)}\u2026` : clean;
|
|
57834
57911
|
}, "compressStr");
|
|
57912
|
+
__name12(normalizeOutcome, "normalizeOutcome");
|
|
57835
57913
|
handleSnapEnd = /* @__PURE__ */ __name12(async (args, context) => {
|
|
57836
57914
|
const validation = validateSnapEndParams(args);
|
|
57837
57915
|
if (!validation.success) {
|
|
@@ -57857,7 +57935,7 @@ ${validation.errors?.join("\n") ?? "Unknown validation error"}`
|
|
|
57857
57935
|
let suggestedCeremony = null;
|
|
57858
57936
|
let ceremonySuggestionReason = "";
|
|
57859
57937
|
let ceremonySuggestionConfidence = 0;
|
|
57860
|
-
const isCleanCompletion = params.outcome === "completed" || params.ok === 1;
|
|
57938
|
+
const isCleanCompletion = normalizeOutcome(params.outcome, params.ok) === "completed" || params.ok === 1;
|
|
57861
57939
|
if (!params.ceremony && context.workspaceRoot && !isCleanCompletion) {
|
|
57862
57940
|
const sessionState = await getSessionStateAsync(context.workspaceRoot);
|
|
57863
57941
|
if (sessionState) {
|
|
@@ -58009,9 +58087,9 @@ ${ceremonyOutput}
|
|
|
58009
58087
|
}
|
|
58010
58088
|
}
|
|
58011
58089
|
const completeArgs = {
|
|
58012
|
-
outcome: params.outcome
|
|
58090
|
+
outcome: normalizeOutcome(params.outcome, params.ok),
|
|
58013
58091
|
createSnapshot: true,
|
|
58014
|
-
notes: params.notes
|
|
58092
|
+
notes: params.notes ?? params.outcome
|
|
58015
58093
|
};
|
|
58016
58094
|
completeArgs.learningsCaptured = directLearningsPersisted;
|
|
58017
58095
|
const result7 = await handleCompleteTask(completeArgs, context);
|
|
@@ -59912,7 +59990,7 @@ var MCP_ROUTES = {
|
|
|
59912
59990
|
}
|
|
59913
59991
|
}
|
|
59914
59992
|
delay(ms) {
|
|
59915
|
-
return new Promise((
|
|
59993
|
+
return new Promise((resolve62) => setTimeout(resolve62, ms));
|
|
59916
59994
|
}
|
|
59917
59995
|
/**
|
|
59918
59996
|
* Get circuit breaker state for monitoring
|
|
@@ -59961,7 +60039,7 @@ var BridgeReceiver = class {
|
|
|
59961
60039
|
console.error("[BridgeReceiver] Already running");
|
|
59962
60040
|
return;
|
|
59963
60041
|
}
|
|
59964
|
-
return new Promise((
|
|
60042
|
+
return new Promise((resolve62, reject) => {
|
|
59965
60043
|
this.server = createServer((req, res) => this.handleRequest(req, res));
|
|
59966
60044
|
this.server.on("error", (err22) => {
|
|
59967
60045
|
console.error("[BridgeReceiver] Server error:", err22);
|
|
@@ -59969,7 +60047,7 @@ var BridgeReceiver = class {
|
|
|
59969
60047
|
});
|
|
59970
60048
|
this.server.listen(this.port, this.host, () => {
|
|
59971
60049
|
console.error(`[BridgeReceiver] Listening on http://${this.host}:${this.port}`);
|
|
59972
|
-
|
|
60050
|
+
resolve62();
|
|
59973
60051
|
});
|
|
59974
60052
|
});
|
|
59975
60053
|
}
|
|
@@ -59980,11 +60058,11 @@ var BridgeReceiver = class {
|
|
|
59980
60058
|
if (!this.server) {
|
|
59981
60059
|
return;
|
|
59982
60060
|
}
|
|
59983
|
-
return new Promise((
|
|
60061
|
+
return new Promise((resolve62) => {
|
|
59984
60062
|
this.server?.close(() => {
|
|
59985
60063
|
console.error("[BridgeReceiver] Stopped");
|
|
59986
60064
|
this.server = null;
|
|
59987
|
-
|
|
60065
|
+
resolve62();
|
|
59988
60066
|
});
|
|
59989
60067
|
});
|
|
59990
60068
|
}
|
|
@@ -60558,11 +60636,11 @@ var SSELocalTransport = class extends EventEmitter {
|
|
|
60558
60636
|
res.end("Not Found");
|
|
60559
60637
|
}
|
|
60560
60638
|
});
|
|
60561
|
-
return new Promise((
|
|
60639
|
+
return new Promise((resolve62, reject) => {
|
|
60562
60640
|
this.server.listen(this.config.port, () => {
|
|
60563
60641
|
this.logger.info(`Listening on localhost:${this.config.port}`);
|
|
60564
60642
|
this.startPingLoop();
|
|
60565
|
-
|
|
60643
|
+
resolve62();
|
|
60566
60644
|
});
|
|
60567
60645
|
this.server.on("error", reject);
|
|
60568
60646
|
});
|
|
@@ -60583,13 +60661,13 @@ var SSELocalTransport = class extends EventEmitter {
|
|
|
60583
60661
|
this.clients.delete(clientId);
|
|
60584
60662
|
}
|
|
60585
60663
|
if (this.server) {
|
|
60586
|
-
return new Promise((
|
|
60664
|
+
return new Promise((resolve62, reject) => {
|
|
60587
60665
|
this.server.close((err22) => {
|
|
60588
60666
|
if (err22) reject(err22);
|
|
60589
60667
|
else {
|
|
60590
60668
|
this.server = null;
|
|
60591
60669
|
this.logger.info("Transport stopped");
|
|
60592
|
-
|
|
60670
|
+
resolve62();
|
|
60593
60671
|
}
|
|
60594
60672
|
});
|
|
60595
60673
|
});
|
|
@@ -62379,18 +62457,18 @@ async function createUnifiedServer(options, injectedRegistry) {
|
|
|
62379
62457
|
}
|
|
62380
62458
|
},
|
|
62381
62459
|
instructions: [
|
|
62382
|
-
"SnapBack MCP \u2014 V2
|
|
62460
|
+
"SnapBack MCP \u2014 V2 4-Tool Surface (authoritative, overrides individual descriptions):",
|
|
62383
62461
|
"",
|
|
62384
|
-
"Workflow:
|
|
62462
|
+
"Workflow: snap_begin \u2192 [work] \u2192 snap_learn (optional) \u2192 snap_end.",
|
|
62385
62463
|
"",
|
|
62386
62464
|
"Tool Usage:",
|
|
62387
|
-
" 1. Call
|
|
62388
|
-
" 2. Call
|
|
62389
|
-
" 3. Call
|
|
62465
|
+
" 1. Call snap_begin FIRST before any code changes \u2014 returns intelligence briefing",
|
|
62466
|
+
" 2. Call snap_learn when you discover something worth remembering",
|
|
62467
|
+
" 3. Call snap_end when task is complete \u2014 returns session summary",
|
|
62390
62468
|
"",
|
|
62391
62469
|
"Key Points:",
|
|
62392
|
-
" -
|
|
62393
|
-
" -
|
|
62470
|
+
" - snap_begin is idempotent \u2014 calling twice resumes the same session",
|
|
62471
|
+
" - snap_learn creates implicit session if needed",
|
|
62394
62472
|
" - All files are automatically protected \u2014 no manual snapshot needed",
|
|
62395
62473
|
" - Workspace isolation: learnings and context are scoped to this workspace"
|
|
62396
62474
|
].join("\n")
|
|
@@ -62703,7 +62781,7 @@ ${learning.action}`,
|
|
|
62703
62781
|
logSuccess(reqCtx, "Tool call completed", {
|
|
62704
62782
|
isError: toolResult.isError
|
|
62705
62783
|
});
|
|
62706
|
-
const shouldEnrich = name !== "meta" && name !== "
|
|
62784
|
+
const shouldEnrich = name !== "meta" && name !== "snap_end";
|
|
62707
62785
|
const enhanced = shouldEnrich ? await vitalsEnricher.enhance(toolResult, workspaceRoot, eventTracker) : toolResult;
|
|
62708
62786
|
const enrichedContent = (enhanced.content || []).map((c) => ({
|
|
62709
62787
|
type: "text",
|
|
@@ -62732,6 +62810,19 @@ ${warningsText}${humanPart}`;
|
|
|
62732
62810
|
}
|
|
62733
62811
|
}
|
|
62734
62812
|
}
|
|
62813
|
+
if (enhanced.isError && name !== "snap_learn" && name !== "snap_end") {
|
|
62814
|
+
const firstContent = enrichedContent[0];
|
|
62815
|
+
if (firstContent && typeof firstContent.text === "string") {
|
|
62816
|
+
const learningHint = [
|
|
62817
|
+
"",
|
|
62818
|
+
"\u{1F4A1} SUGGESTED_LEARNING: This tool call failed. Consider capturing the failure pattern:",
|
|
62819
|
+
` snap_learn({ type: "pitfall", trigger: "${name} error", action: "..." })`,
|
|
62820
|
+
" (Replace '...' with the specific insight from this failure)",
|
|
62821
|
+
""
|
|
62822
|
+
].join("\n");
|
|
62823
|
+
firstContent.text = `${firstContent.text}${learningHint}`;
|
|
62824
|
+
}
|
|
62825
|
+
}
|
|
62735
62826
|
return {
|
|
62736
62827
|
content: enrichedContent,
|
|
62737
62828
|
isError: enhanced.isError
|
|
@@ -64235,7 +64326,7 @@ async function autoConfigureTools(options) {
|
|
|
64235
64326
|
if (detection.detected.length === 0) {
|
|
64236
64327
|
if (!jsonOutput) {
|
|
64237
64328
|
console.log(chalk37.yellow("\nNo AI tools detected"));
|
|
64238
|
-
console.log(chalk37.gray("Install one of these to use SnapBack MCP:"));
|
|
64329
|
+
console.log(chalk37.gray("Install one of these to use \u{1F9E2} SnapBack MCP:"));
|
|
64239
64330
|
console.log(chalk37.gray(" \u2022 Claude Desktop - https://claude.ai/download"));
|
|
64240
64331
|
console.log(chalk37.gray(" \u2022 Cursor - https://cursor.sh"));
|
|
64241
64332
|
console.log(chalk37.gray(" \u2022 Windsurf - https://codeium.com/windsurf"));
|
|
@@ -64252,7 +64343,7 @@ async function autoConfigureTools(options) {
|
|
|
64252
64343
|
}
|
|
64253
64344
|
if (needsSetup.length === 0) {
|
|
64254
64345
|
if (!jsonOutput) {
|
|
64255
|
-
console.log(chalk37.green("\n\u2713 All detected AI tools already have SnapBack configured!"));
|
|
64346
|
+
console.log(chalk37.green("\n\u2713 All detected AI tools already have \u{1F9E2} SnapBack configured!"));
|
|
64256
64347
|
console.log(chalk37.gray("Use --force to reconfigure."));
|
|
64257
64348
|
showNextSteps();
|
|
64258
64349
|
}
|
|
@@ -64270,7 +64361,7 @@ Detected ${detection.detected.length} AI tool(s):`));
|
|
|
64270
64361
|
if (!skipPrompts && !jsonOutput) {
|
|
64271
64362
|
const clientNames = needsSetup.map((c) => c.displayName).join(", ");
|
|
64272
64363
|
const proceed = await confirm({
|
|
64273
|
-
message: `Configure SnapBack for ${clientNames}?`,
|
|
64364
|
+
message: `Configure \u{1F9E2} SnapBack for ${clientNames}?`,
|
|
64274
64365
|
default: true
|
|
64275
64366
|
});
|
|
64276
64367
|
if (!proceed) {
|
|
@@ -64326,17 +64417,29 @@ async function configureClient(client2, dryRun, apiKey, devMode = false, npmMode
|
|
|
64326
64417
|
try {
|
|
64327
64418
|
const workspaceRoot = workspaceOverride || findWorkspaceRoot3(process.cwd());
|
|
64328
64419
|
const workspaceConfig = detectWorkspaceConfig(workspaceRoot);
|
|
64420
|
+
let localCliPath;
|
|
64329
64421
|
if (workspaceConfig && isGlobalConfig(client2.configPath)) {
|
|
64330
|
-
|
|
64331
|
-
|
|
64332
|
-
|
|
64333
|
-
|
|
64334
|
-
|
|
64335
|
-
|
|
64336
|
-
|
|
64337
|
-
|
|
64422
|
+
if (client2.format === "qoder" && client2.hasSnapback) {
|
|
64423
|
+
const existingValidation = validateClientConfig(client2);
|
|
64424
|
+
if (existingValidation.valid) {
|
|
64425
|
+
spinner2.info(`${client2.displayName} workspace config detected (global config valid, skipping)`);
|
|
64426
|
+
return;
|
|
64427
|
+
}
|
|
64428
|
+
spinner2.text = `Repairing broken global ${client2.displayName} config...`;
|
|
64429
|
+
devMode = false;
|
|
64430
|
+
npmMode = false;
|
|
64431
|
+
localCliPath = void 0;
|
|
64432
|
+
} else {
|
|
64433
|
+
spinner2.info(`${client2.displayName} workspace config detected`);
|
|
64434
|
+
console.log(chalk37.gray(` Workspace: ${workspaceConfig.path}`));
|
|
64435
|
+
console.log(chalk37.gray(` Type: ${workspaceConfig.type}`));
|
|
64436
|
+
console.log();
|
|
64437
|
+
console.log(chalk37.cyan(" Skipping global config to prevent conflicts"));
|
|
64438
|
+
console.log(chalk37.gray(" Workspace configurations take precedence over global"));
|
|
64439
|
+
console.log();
|
|
64440
|
+
return;
|
|
64441
|
+
}
|
|
64338
64442
|
}
|
|
64339
|
-
let localCliPath;
|
|
64340
64443
|
if (devMode) {
|
|
64341
64444
|
localCliPath = findCliDistPath(workspaceRoot);
|
|
64342
64445
|
if (!localCliPath) {
|
|
@@ -64426,12 +64529,24 @@ async function configureClientWithResult(client2, dryRun, apiKey, devMode = fals
|
|
|
64426
64529
|
try {
|
|
64427
64530
|
const workspaceRoot = workspaceOverride || findWorkspaceRoot3(process.cwd());
|
|
64428
64531
|
const workspaceConfig = detectWorkspaceConfig(workspaceRoot);
|
|
64532
|
+
let localCliPath;
|
|
64429
64533
|
if (workspaceConfig && isGlobalConfig(client2.configPath)) {
|
|
64430
|
-
|
|
64431
|
-
|
|
64432
|
-
|
|
64534
|
+
if (client2.format === "qoder" && client2.hasSnapback) {
|
|
64535
|
+
const existingValidation = validateClientConfig(client2);
|
|
64536
|
+
if (existingValidation.valid) {
|
|
64537
|
+
return {
|
|
64538
|
+
success: true
|
|
64539
|
+
};
|
|
64540
|
+
}
|
|
64541
|
+
devMode = false;
|
|
64542
|
+
npmMode = false;
|
|
64543
|
+
localCliPath = void 0;
|
|
64544
|
+
} else {
|
|
64545
|
+
return {
|
|
64546
|
+
success: true
|
|
64547
|
+
};
|
|
64548
|
+
}
|
|
64433
64549
|
}
|
|
64434
|
-
let localCliPath;
|
|
64435
64550
|
if (devMode) {
|
|
64436
64551
|
localCliPath = findCliDistPath(workspaceRoot);
|
|
64437
64552
|
if (!localCliPath) {
|
|
@@ -64526,7 +64641,7 @@ async function resolveApiKey(providedApiKey, skipPrompts = false) {
|
|
|
64526
64641
|
}
|
|
64527
64642
|
if (!skipPrompts) {
|
|
64528
64643
|
const wantApiKey = await confirm({
|
|
64529
|
-
message: "Do you have a SnapBack API key for Pro features?",
|
|
64644
|
+
message: "Do you have a \u{1F9E2} SnapBack API key for Pro features?",
|
|
64530
64645
|
default: false
|
|
64531
64646
|
});
|
|
64532
64647
|
if (wantApiKey) {
|
|
@@ -64606,7 +64721,7 @@ async function validateTools(verbose = false) {
|
|
|
64606
64721
|
const detection = detectAIClients();
|
|
64607
64722
|
const configured = detection.detected.filter((c) => c.hasSnapback);
|
|
64608
64723
|
if (configured.length === 0) {
|
|
64609
|
-
console.log(chalk37.yellow("\nNo AI tools with SnapBack configured."));
|
|
64724
|
+
console.log(chalk37.yellow("\nNo AI tools with \u{1F9E2} SnapBack configured."));
|
|
64610
64725
|
console.log(chalk37.gray("Run: snap tools configure"));
|
|
64611
64726
|
return;
|
|
64612
64727
|
}
|
|
@@ -64658,7 +64773,7 @@ async function repairTools(skipPrompts = false, workspaceOverride, providedApiKe
|
|
|
64658
64773
|
const detection = detectAIClients();
|
|
64659
64774
|
const configured = detection.detected.filter((c) => c.hasSnapback);
|
|
64660
64775
|
if (configured.length === 0) {
|
|
64661
|
-
console.log(chalk37.yellow("\nNo AI tools with SnapBack configured."));
|
|
64776
|
+
console.log(chalk37.yellow("\nNo AI tools with \u{1F9E2} SnapBack configured."));
|
|
64662
64777
|
console.log(chalk37.gray("Run: snap tools configure"));
|
|
64663
64778
|
return;
|
|
64664
64779
|
}
|
|
@@ -64739,8 +64854,8 @@ function showNextSteps() {
|
|
|
64739
64854
|
console.log(chalk37.bold("Next Steps:"));
|
|
64740
64855
|
console.log();
|
|
64741
64856
|
console.log(" 1. Restart your AI assistant (Claude Desktop, Cursor, etc.)");
|
|
64742
|
-
console.log(' 2. Ask your AI: "What does SnapBack know about this project?"');
|
|
64743
|
-
console.log(' 3. Before risky changes, ask: "Create a SnapBack checkpoint"');
|
|
64857
|
+
console.log(' 2. Ask your AI: "What does \u{1F9E2} SnapBack know about this project?"');
|
|
64858
|
+
console.log(' 3. Before risky changes, ask: "Create a \u{1F9E2} SnapBack checkpoint"');
|
|
64744
64859
|
console.log();
|
|
64745
64860
|
console.log(chalk37.dim("Available tools your AI can now use:"));
|
|
64746
64861
|
console.log(chalk37.dim(" \u2022 snapback.get_context - Understand your codebase"));
|
|
@@ -66194,13 +66309,13 @@ async function addAgentToConfig(configPath, agentName, agentConfig) {
|
|
|
66194
66309
|
__name(addAgentToConfig, "addAgentToConfig");
|
|
66195
66310
|
var acpCommand = createAcpCommand();
|
|
66196
66311
|
function registerDaemonCommands(program) {
|
|
66197
|
-
const daemon = program.command("daemon").description("Manage SnapBack daemon");
|
|
66198
|
-
daemon.command("start").description("Start the SnapBack daemon").option("--no-detach", "Run daemon in foreground (attached to terminal)").option("-f, --foreground", "Same as --no-detach").option("-t, --idle-timeout <minutes>", "Shutdown after idle (default: 240)", "240").action(async (options) => {
|
|
66312
|
+
const daemon = program.command("daemon").description("Manage \u{1F9E2} SnapBack daemon");
|
|
66313
|
+
daemon.command("start").description("Start the \u{1F9E2} SnapBack daemon").option("--no-detach", "Run daemon in foreground (attached to terminal)").option("-f, --foreground", "Same as --no-detach").option("-t, --idle-timeout <minutes>", "Shutdown after idle (default: 240)", "240").action(async (options) => {
|
|
66199
66314
|
if (isServiceRunning()) {
|
|
66200
66315
|
console.log("\u2713 Daemon is already running");
|
|
66201
66316
|
return;
|
|
66202
66317
|
}
|
|
66203
|
-
console.log("Starting SnapBack daemon...");
|
|
66318
|
+
console.log("Starting \u{1F9E2} SnapBack daemon...");
|
|
66204
66319
|
const snapbackdArgs = [
|
|
66205
66320
|
"--idle-timeout",
|
|
66206
66321
|
options.idleTimeout
|
|
@@ -66307,7 +66422,7 @@ function registerDaemonCommands(program) {
|
|
|
66307
66422
|
child.on("error", reject);
|
|
66308
66423
|
});
|
|
66309
66424
|
});
|
|
66310
|
-
daemon.command("stop").description("Stop the SnapBack daemon").action(async () => {
|
|
66425
|
+
daemon.command("stop").description("Stop the \u{1F9E2} SnapBack daemon").action(async () => {
|
|
66311
66426
|
if (!isServiceRunning()) {
|
|
66312
66427
|
console.log("Daemon is not running");
|
|
66313
66428
|
return;
|
|
@@ -67702,7 +67817,7 @@ function formatValue(value) {
|
|
|
67702
67817
|
return String(value);
|
|
67703
67818
|
}
|
|
67704
67819
|
__name(formatValue, "formatValue");
|
|
67705
|
-
var cliVersion2 = "
|
|
67820
|
+
var cliVersion2 = "3.0.0" ;
|
|
67706
67821
|
function createDoctorCommand() {
|
|
67707
67822
|
return new Command("doctor").description("Diagnose SnapBack installation and ecosystem health").option("--json", "Output structured JSON result").option("--local", "Skip network checks").option("--check <system>", "Check specific subsystem (cli|daemon|workspace|knowledge|mcp|network|extension)").option("--fix", "Attempt automatic repair for known issues").option("-q, --quiet", "Only show failures").option("-v, --verbose", "Show all check details").action(async (options) => {
|
|
67708
67823
|
const result7 = await runDoctor(options);
|
|
@@ -67721,7 +67836,7 @@ async function runDoctor(options) {
|
|
|
67721
67836
|
const workspacePath = findWorkspaceRoot2(process.cwd());
|
|
67722
67837
|
if (!jsonMode) {
|
|
67723
67838
|
console.log();
|
|
67724
|
-
console.log(chalk37.cyan(` SnapBack Doctor v${cliVersion2}`));
|
|
67839
|
+
console.log(chalk37.cyan(` \u{1F9E2} SnapBack Doctor v${cliVersion2}`));
|
|
67725
67840
|
console.log(chalk37.gray(` Platform: ${platformLabel()}, Node ${process.version}`));
|
|
67726
67841
|
console.log();
|
|
67727
67842
|
}
|
|
@@ -67794,9 +67909,9 @@ async function runDoctor(options) {
|
|
|
67794
67909
|
if (!jsonMode) {
|
|
67795
67910
|
console.log(chalk37.gray(` Summary: ${summary.pass} passed, ${summary.warn} warning${summary.warn !== 1 ? "s" : ""}, ${summary.fail} failure${summary.fail !== 1 ? "s" : ""}`));
|
|
67796
67911
|
if (success) {
|
|
67797
|
-
console.log(chalk37.green(" \u2714 SnapBack is healthy"));
|
|
67912
|
+
console.log(chalk37.green(" \u2714 \u{1F9E2} SnapBack is healthy"));
|
|
67798
67913
|
} else {
|
|
67799
|
-
console.log(chalk37.red(" \u2716 SnapBack has issues \u2014 run the suggested fixes above"));
|
|
67914
|
+
console.log(chalk37.red(" \u2716 \u{1F9E2} SnapBack has issues \u2014 run the suggested fixes above"));
|
|
67800
67915
|
}
|
|
67801
67916
|
console.log();
|
|
67802
67917
|
}
|
|
@@ -68730,7 +68845,7 @@ Latest: v${versionInfo.latest}`,
|
|
|
68730
68845
|
}
|
|
68731
68846
|
__name(createUpgradeCommand, "createUpgradeCommand");
|
|
68732
68847
|
async function getVersionInfo(canary = false) {
|
|
68733
|
-
const current = "
|
|
68848
|
+
const current = "3.0.0";
|
|
68734
68849
|
let latest = current;
|
|
68735
68850
|
try {
|
|
68736
68851
|
const tag = canary ? "canary" : "latest";
|
|
@@ -68990,6 +69105,59 @@ ${chalk37.dim("Learn more: https://docs.snapback.dev")}
|
|
|
68990
69105
|
__name(displayWelcomeMessage, "displayWelcomeMessage");
|
|
68991
69106
|
|
|
68992
69107
|
// src/commands/wizard.ts
|
|
69108
|
+
var SNAPBACK_AI_INSTRUCTIONS = `## SnapBack Intelligence
|
|
69109
|
+
|
|
69110
|
+
Always call \`snap_begin\` before any coding task. It opens a session and returns
|
|
69111
|
+
codebase intelligence (fragile files, co-change patterns, past learnings) that
|
|
69112
|
+
your context window does not have. Without it: no session tracking, no automatic
|
|
69113
|
+
snapshots, snap_pulse and snap_end return empty results.
|
|
69114
|
+
|
|
69115
|
+
### Tool Sequence
|
|
69116
|
+
1. **snap_begin** \u2190 Always first, before file reads or any other tool
|
|
69117
|
+
2. [your work tools]
|
|
69118
|
+
3. **snap_pulse** \u2190 After 3+ tool calls, when uncertain, before clarifying questions
|
|
69119
|
+
4. **snap_learn** \u2190 When user corrects you, or you discover unexpected codebase patterns
|
|
69120
|
+
5. **snap_end** \u2190 After task completion, before your final message
|
|
69121
|
+
|
|
69122
|
+
### Without snap_begin
|
|
69123
|
+
- No session = no automatic snapshots
|
|
69124
|
+
- No intelligence context = blind to codebase risks
|
|
69125
|
+
- snap_pulse and snap_end return empty results
|
|
69126
|
+
`;
|
|
69127
|
+
function writeWorkspaceAIInstructions(workspaceRoot) {
|
|
69128
|
+
const targets = [
|
|
69129
|
+
{
|
|
69130
|
+
file: "CLAUDE.md",
|
|
69131
|
+
header: "## SnapBack Intelligence"
|
|
69132
|
+
},
|
|
69133
|
+
{
|
|
69134
|
+
file: ".cursorrules",
|
|
69135
|
+
header: "## SnapBack Intelligence"
|
|
69136
|
+
},
|
|
69137
|
+
{
|
|
69138
|
+
file: ".windsurfrules",
|
|
69139
|
+
header: "## SnapBack Intelligence"
|
|
69140
|
+
}
|
|
69141
|
+
];
|
|
69142
|
+
for (const { file, header } of targets) {
|
|
69143
|
+
const filePath = path10.join(workspaceRoot, file);
|
|
69144
|
+
try {
|
|
69145
|
+
if (fs32.existsSync(filePath)) {
|
|
69146
|
+
const existing = fs32.readFileSync(filePath, "utf-8");
|
|
69147
|
+
if (existing.includes(header)) continue;
|
|
69148
|
+
fs32.appendFileSync(filePath, `
|
|
69149
|
+
|
|
69150
|
+
${SNAPBACK_AI_INSTRUCTIONS}`, "utf-8");
|
|
69151
|
+
} else {
|
|
69152
|
+
fs32.writeFileSync(filePath, SNAPBACK_AI_INSTRUCTIONS, "utf-8");
|
|
69153
|
+
}
|
|
69154
|
+
process.stdout.write(chalk37.gray(` ${file} written \u2713
|
|
69155
|
+
`));
|
|
69156
|
+
} catch {
|
|
69157
|
+
}
|
|
69158
|
+
}
|
|
69159
|
+
}
|
|
69160
|
+
__name(writeWorkspaceAIInstructions, "writeWorkspaceAIInstructions");
|
|
68993
69161
|
function detectProjectType(cwd) {
|
|
68994
69162
|
const indicators = [];
|
|
68995
69163
|
let type = "unknown";
|
|
@@ -69281,7 +69449,7 @@ ${chalk37.cyan.bold("Step 4: AI Tool Integration\n")}`);
|
|
|
69281
69449
|
let getSnapbackMCPConfig2;
|
|
69282
69450
|
let writeClientConfig2;
|
|
69283
69451
|
try {
|
|
69284
|
-
const mcpConfig = await import('./dist-
|
|
69452
|
+
const mcpConfig = await import('./dist-5LR7APG5.js');
|
|
69285
69453
|
detectAIClients2 = mcpConfig.detectAIClients;
|
|
69286
69454
|
getSnapbackMCPConfig2 = mcpConfig.getSnapbackMCPConfig;
|
|
69287
69455
|
writeClientConfig2 = mcpConfig.writeClientConfig;
|
|
@@ -69354,6 +69522,9 @@ ${chalk37.cyan.bold("Step 4: AI Tool Integration\n")}`);
|
|
|
69354
69522
|
console.log(chalk37.red(` \u2717 ${result7.error}`));
|
|
69355
69523
|
}
|
|
69356
69524
|
}
|
|
69525
|
+
if (state.workspaceRoot) {
|
|
69526
|
+
writeWorkspaceAIInstructions(state.workspaceRoot);
|
|
69527
|
+
}
|
|
69357
69528
|
console.log();
|
|
69358
69529
|
status.success("MCP integration configured!");
|
|
69359
69530
|
console.log(chalk37.gray(" Restart your AI tools to activate SnapBack."));
|
|
@@ -69644,7 +69815,7 @@ ${chalk37.green.bold("Setup Complete! \u{1F389}\n")}`);
|
|
|
69644
69815
|
console.log(`
|
|
69645
69816
|
${chalk37.cyan.bold("Quick Health Check:\n")}`);
|
|
69646
69817
|
try {
|
|
69647
|
-
const { detectAIClients: detectAIClients2 } = await import('./dist-
|
|
69818
|
+
const { detectAIClients: detectAIClients2 } = await import('./dist-5LR7APG5.js');
|
|
69648
69819
|
const detection = detectAIClients2({
|
|
69649
69820
|
cwd: state.workspaceRoot || process.cwd()
|
|
69650
69821
|
});
|
|
@@ -69659,7 +69830,7 @@ ${chalk37.cyan.bold("Quick Health Check:\n")}`);
|
|
|
69659
69830
|
} catch {
|
|
69660
69831
|
}
|
|
69661
69832
|
try {
|
|
69662
|
-
const { detectAIClients: detectAIClients2 } = await import('./dist-
|
|
69833
|
+
const { detectAIClients: detectAIClients2 } = await import('./dist-5LR7APG5.js');
|
|
69663
69834
|
const detection = detectAIClients2({
|
|
69664
69835
|
cwd: state.workspaceRoot || process.cwd()
|
|
69665
69836
|
});
|