@driftless-sh/cli 0.1.32 → 0.1.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +366 -450
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -33,98 +33,133 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
33
33
|
mod
|
|
34
34
|
));
|
|
35
35
|
|
|
36
|
-
// src/
|
|
37
|
-
var
|
|
38
|
-
__export(
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
getLastCommitHash: () => getLastCommitHash,
|
|
44
|
-
getStagedDiff: () => getStagedDiff,
|
|
45
|
-
getUncommittedDiff: () => getUncommittedDiff,
|
|
46
|
-
isGitRepo: () => isGitRepo
|
|
36
|
+
// src/api-client.ts
|
|
37
|
+
var api_client_exports = {};
|
|
38
|
+
__export(api_client_exports, {
|
|
39
|
+
api: () => api,
|
|
40
|
+
formatError: () => formatError,
|
|
41
|
+
getApiKey: () => getApiKey,
|
|
42
|
+
getApiUrl: () => getApiUrl
|
|
47
43
|
});
|
|
48
|
-
function
|
|
44
|
+
function loadApiKey() {
|
|
45
|
+
const envKey = process.env["DRIFTLESS_API_KEY"];
|
|
46
|
+
if (envKey) return envKey;
|
|
49
47
|
try {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}).trim();
|
|
54
|
-
const match = url.match(/[:/]([^/]+)\/([^/]+?)(?:\.git)?$/);
|
|
55
|
-
if (match) {
|
|
56
|
-
return { org: match[1], repo: match[2] };
|
|
48
|
+
if ((0, import_node_fs.existsSync)(CONFIG_PATH)) {
|
|
49
|
+
const config = JSON.parse((0, import_node_fs.readFileSync)(CONFIG_PATH, "utf8"));
|
|
50
|
+
return config.api_key || null;
|
|
57
51
|
}
|
|
58
|
-
return null;
|
|
59
52
|
} catch {
|
|
60
|
-
return null;
|
|
61
53
|
}
|
|
54
|
+
return null;
|
|
62
55
|
}
|
|
63
|
-
function
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
try {
|
|
68
|
-
return (0, import_node_child_process.execSync)("git diff", { encoding: "utf8", cwd: process.cwd() }).trim();
|
|
69
|
-
} catch {
|
|
70
|
-
return "";
|
|
56
|
+
function getBaseUrl() {
|
|
57
|
+
const envUrl = process.env["DRIFTLESS_API_URL"];
|
|
58
|
+
if (envUrl) {
|
|
59
|
+
return envUrl.endsWith("/api/v1") ? envUrl : `${envUrl}/api/v1`;
|
|
71
60
|
}
|
|
72
|
-
}
|
|
73
|
-
function getStagedDiff() {
|
|
74
61
|
try {
|
|
75
|
-
|
|
62
|
+
if ((0, import_node_fs.existsSync)(CONFIG_PATH)) {
|
|
63
|
+
const config = JSON.parse((0, import_node_fs.readFileSync)(CONFIG_PATH, "utf8"));
|
|
64
|
+
return config.api_url || DEFAULT_URL;
|
|
65
|
+
}
|
|
76
66
|
} catch {
|
|
77
|
-
return "";
|
|
78
67
|
}
|
|
68
|
+
return DEFAULT_URL;
|
|
79
69
|
}
|
|
80
|
-
function
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
70
|
+
function parseError(e) {
|
|
71
|
+
const msg = e.message;
|
|
72
|
+
const jsonMatch = msg.match(/\{[\s\S]*\}/);
|
|
73
|
+
if (jsonMatch) {
|
|
74
|
+
try {
|
|
75
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
76
|
+
const parts = [];
|
|
77
|
+
if (parsed.message) parts.push(parsed.message);
|
|
78
|
+
if (parsed.request_id) parts.push(`request_id: ${parsed.request_id}`);
|
|
79
|
+
if (parsed.retryable !== void 0) parts.push(`retryable: ${parsed.retryable ? "yes" : "no"}`);
|
|
80
|
+
if (parsed.endpoint) parts.push(`endpoint: ${parsed.endpoint}`);
|
|
81
|
+
if (parts.length > 0) return parts.join(" | ");
|
|
82
|
+
} catch {
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const statusMatch = msg.match(/HTTP (\d+):/);
|
|
86
|
+
if (statusMatch) {
|
|
87
|
+
const code = parseInt(statusMatch[1], 10);
|
|
88
|
+
if (code === 500) return "Internal server error \u2014 the API encountered an unexpected issue";
|
|
89
|
+
if (code === 401) return "Authentication failed \u2014 check your API key";
|
|
90
|
+
if (code === 403) return "Access denied \u2014 your API key lacks permission";
|
|
91
|
+
if (code === 404) return "Not found \u2014 the resource does not exist";
|
|
92
|
+
if (code === 429) return "Rate limited \u2014 too many requests, try again later";
|
|
93
|
+
return `Server error (HTTP ${code})`;
|
|
85
94
|
}
|
|
95
|
+
return msg;
|
|
86
96
|
}
|
|
87
|
-
function
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
97
|
+
function request(method, path, body) {
|
|
98
|
+
return new Promise((resolve8, reject) => {
|
|
99
|
+
const baseUrl = getBaseUrl();
|
|
100
|
+
const fullUrl = `${baseUrl}${path}`;
|
|
101
|
+
const url = new URL(fullUrl);
|
|
102
|
+
const isHttps = url.protocol === "https:";
|
|
103
|
+
const fn = isHttps ? import_node_https.request : import_node_http.request;
|
|
104
|
+
const headers = {
|
|
105
|
+
"Content-Type": "application/json",
|
|
106
|
+
Accept: "application/json"
|
|
107
|
+
};
|
|
108
|
+
const apiKey = loadApiKey();
|
|
109
|
+
if (apiKey) {
|
|
110
|
+
headers["X-API-Key"] = apiKey;
|
|
98
111
|
}
|
|
99
|
-
|
|
100
|
-
|
|
112
|
+
const req = fn(
|
|
113
|
+
fullUrl,
|
|
114
|
+
{ method, headers },
|
|
115
|
+
(res) => {
|
|
116
|
+
let data = "";
|
|
117
|
+
res.on("data", (chunk) => data += chunk.toString());
|
|
118
|
+
res.on("end", () => {
|
|
119
|
+
if (res.statusCode && res.statusCode >= 400) {
|
|
120
|
+
reject(new Error(`HTTP ${res.statusCode}: ${data}`));
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
try {
|
|
124
|
+
resolve8(JSON.parse(data));
|
|
125
|
+
} catch {
|
|
126
|
+
resolve8(data);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
);
|
|
131
|
+
req.on("error", (e) => reject(new Error(`Connection failed: ${e.message}`)));
|
|
132
|
+
if (body) req.write(JSON.stringify(body));
|
|
133
|
+
req.end();
|
|
134
|
+
});
|
|
101
135
|
}
|
|
102
|
-
function
|
|
103
|
-
|
|
104
|
-
const unstaged = (0, import_node_child_process.execSync)("git diff --name-only HEAD", { encoding: "utf8", cwd: process.cwd() }).trim();
|
|
105
|
-
const staged = (0, import_node_child_process.execSync)("git diff --staged --name-only", { encoding: "utf8", cwd: process.cwd() }).trim();
|
|
106
|
-
const files = /* @__PURE__ */ new Set();
|
|
107
|
-
if (unstaged) unstaged.split("\n").forEach((f) => files.add(f));
|
|
108
|
-
if (staged) staged.split("\n").forEach((f) => files.add(f));
|
|
109
|
-
return [...files].filter(Boolean);
|
|
110
|
-
} catch {
|
|
111
|
-
return [];
|
|
112
|
-
}
|
|
136
|
+
function getApiUrl() {
|
|
137
|
+
return getBaseUrl();
|
|
113
138
|
}
|
|
114
|
-
function
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
139
|
+
function getApiKey() {
|
|
140
|
+
return loadApiKey();
|
|
141
|
+
}
|
|
142
|
+
function formatError(e) {
|
|
143
|
+
return parseError(e);
|
|
120
144
|
}
|
|
121
|
-
var
|
|
122
|
-
var
|
|
123
|
-
"src/
|
|
145
|
+
var import_node_http, import_node_https, import_node_fs, import_node_path, import_node_os, CONFIG_PATH, DEFAULT_URL, api;
|
|
146
|
+
var init_api_client = __esm({
|
|
147
|
+
"src/api-client.ts"() {
|
|
124
148
|
"use strict";
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
149
|
+
import_node_http = require("node:http");
|
|
150
|
+
import_node_https = require("node:https");
|
|
151
|
+
import_node_fs = require("node:fs");
|
|
152
|
+
import_node_path = require("node:path");
|
|
153
|
+
import_node_os = require("node:os");
|
|
154
|
+
CONFIG_PATH = (0, import_node_path.resolve)((0, import_node_os.homedir)(), ".driftless", "config.json");
|
|
155
|
+
DEFAULT_URL = "http://localhost:3000/api/v1";
|
|
156
|
+
api = {
|
|
157
|
+
get: (path) => request("GET", path),
|
|
158
|
+
post: (path, body) => request("POST", path, body),
|
|
159
|
+
put: (path, body) => request("PUT", path, body),
|
|
160
|
+
patch: (path, body) => request("PATCH", path, body),
|
|
161
|
+
delete: (path) => request("DELETE", path)
|
|
162
|
+
};
|
|
128
163
|
}
|
|
129
164
|
});
|
|
130
165
|
|
|
@@ -126242,7 +126277,7 @@ ${lanes.join("\n")}
|
|
|
126242
126277
|
}
|
|
126243
126278
|
}
|
|
126244
126279
|
function createImportCallExpressionAMD(arg, containsLexicalThis) {
|
|
126245
|
-
const
|
|
126280
|
+
const resolve8 = factory2.createUniqueName("resolve");
|
|
126246
126281
|
const reject = factory2.createUniqueName("reject");
|
|
126247
126282
|
const parameters = [
|
|
126248
126283
|
factory2.createParameterDeclaration(
|
|
@@ -126251,7 +126286,7 @@ ${lanes.join("\n")}
|
|
|
126251
126286
|
/*dotDotDotToken*/
|
|
126252
126287
|
void 0,
|
|
126253
126288
|
/*name*/
|
|
126254
|
-
|
|
126289
|
+
resolve8
|
|
126255
126290
|
),
|
|
126256
126291
|
factory2.createParameterDeclaration(
|
|
126257
126292
|
/*modifiers*/
|
|
@@ -126268,7 +126303,7 @@ ${lanes.join("\n")}
|
|
|
126268
126303
|
factory2.createIdentifier("require"),
|
|
126269
126304
|
/*typeArguments*/
|
|
126270
126305
|
void 0,
|
|
126271
|
-
[factory2.createArrayLiteralExpression([arg || factory2.createOmittedExpression()]),
|
|
126306
|
+
[factory2.createArrayLiteralExpression([arg || factory2.createOmittedExpression()]), resolve8, reject]
|
|
126272
126307
|
)
|
|
126273
126308
|
)
|
|
126274
126309
|
]);
|
|
@@ -212986,8 +213021,8 @@ Additional information: BADCLIENT: Bad error code, ${badCode} not found in range
|
|
|
212986
213021
|
installPackage(options) {
|
|
212987
213022
|
this.packageInstallId++;
|
|
212988
213023
|
const request2 = { kind: "installPackage", ...options, id: this.packageInstallId };
|
|
212989
|
-
const promise = new Promise((
|
|
212990
|
-
(this.packageInstalledPromise ?? (this.packageInstalledPromise = /* @__PURE__ */ new Map())).set(this.packageInstallId, { resolve:
|
|
213024
|
+
const promise = new Promise((resolve8, reject) => {
|
|
213025
|
+
(this.packageInstalledPromise ?? (this.packageInstalledPromise = /* @__PURE__ */ new Map())).set(this.packageInstallId, { resolve: resolve8, reject });
|
|
212991
213026
|
});
|
|
212992
213027
|
this.installer.send(request2);
|
|
212993
213028
|
return promise;
|
|
@@ -214311,128 +214346,112 @@ var require_dist = __commonJS({
|
|
|
214311
214346
|
}
|
|
214312
214347
|
});
|
|
214313
214348
|
|
|
214314
|
-
// src/
|
|
214315
|
-
var
|
|
214316
|
-
|
|
214317
|
-
|
|
214318
|
-
|
|
214319
|
-
|
|
214320
|
-
|
|
214321
|
-
|
|
214322
|
-
|
|
214323
|
-
|
|
214349
|
+
// src/analytics.ts
|
|
214350
|
+
var analytics_exports = {};
|
|
214351
|
+
__export(analytics_exports, {
|
|
214352
|
+
analyticsEvent: () => analyticsEvent
|
|
214353
|
+
});
|
|
214354
|
+
function analyticsEvent(event, distinctId, props = {}) {
|
|
214355
|
+
if (!KEY) return;
|
|
214356
|
+
fetch(`${HOST}/capture/`, {
|
|
214357
|
+
method: "POST",
|
|
214358
|
+
headers: { "Content-Type": "application/json" },
|
|
214359
|
+
body: JSON.stringify({
|
|
214360
|
+
api_key: KEY,
|
|
214361
|
+
event,
|
|
214362
|
+
distinct_id: `workspace:${distinctId}`,
|
|
214363
|
+
properties: { ...props, source: "cli", $lib: "driftless-cli" }
|
|
214364
|
+
})
|
|
214365
|
+
}).catch(() => void 0);
|
|
214366
|
+
}
|
|
214367
|
+
var KEY, HOST;
|
|
214368
|
+
var init_analytics = __esm({
|
|
214369
|
+
"src/analytics.ts"() {
|
|
214370
|
+
"use strict";
|
|
214371
|
+
KEY = "";
|
|
214372
|
+
HOST = "https://us.i.posthog.com";
|
|
214373
|
+
}
|
|
214374
|
+
});
|
|
214375
|
+
|
|
214376
|
+
// src/commands/init.ts
|
|
214377
|
+
init_api_client();
|
|
214378
|
+
|
|
214379
|
+
// src/git.ts
|
|
214380
|
+
var import_node_child_process = require("node:child_process");
|
|
214381
|
+
var import_node_fs2 = require("node:fs");
|
|
214382
|
+
var import_node_path2 = require("node:path");
|
|
214383
|
+
function isGitInstalled() {
|
|
214324
214384
|
try {
|
|
214325
|
-
|
|
214326
|
-
|
|
214327
|
-
return config.api_key || null;
|
|
214328
|
-
}
|
|
214385
|
+
(0, import_node_child_process.execSync)("git --version", { stdio: "ignore" });
|
|
214386
|
+
return true;
|
|
214329
214387
|
} catch {
|
|
214388
|
+
return false;
|
|
214330
214389
|
}
|
|
214331
|
-
return null;
|
|
214332
214390
|
}
|
|
214333
|
-
|
|
214334
|
-
function getBaseUrl() {
|
|
214335
|
-
const envUrl = process.env["DRIFTLESS_API_URL"];
|
|
214336
|
-
if (envUrl) {
|
|
214337
|
-
return envUrl.endsWith("/api/v1") ? envUrl : `${envUrl}/api/v1`;
|
|
214338
|
-
}
|
|
214391
|
+
function getGitRemote() {
|
|
214339
214392
|
try {
|
|
214340
|
-
|
|
214341
|
-
|
|
214342
|
-
|
|
214393
|
+
const url = (0, import_node_child_process.execSync)("git config --get remote.origin.url", {
|
|
214394
|
+
encoding: "utf8",
|
|
214395
|
+
cwd: process.cwd()
|
|
214396
|
+
}).trim();
|
|
214397
|
+
const match = url.match(/[:/]([^/]+)\/([^/]+?)(?:\.git)?$/);
|
|
214398
|
+
if (match) {
|
|
214399
|
+
return { org: match[1], repo: match[2] };
|
|
214343
214400
|
}
|
|
214401
|
+
return null;
|
|
214344
214402
|
} catch {
|
|
214403
|
+
return null;
|
|
214345
214404
|
}
|
|
214346
|
-
return DEFAULT_URL;
|
|
214347
214405
|
}
|
|
214348
|
-
function
|
|
214349
|
-
|
|
214350
|
-
|
|
214351
|
-
|
|
214352
|
-
|
|
214353
|
-
|
|
214354
|
-
|
|
214355
|
-
|
|
214356
|
-
if (parsed.request_id) parts.push(`request_id: ${parsed.request_id}`);
|
|
214357
|
-
if (parsed.retryable !== void 0) parts.push(`retryable: ${parsed.retryable ? "yes" : "no"}`);
|
|
214358
|
-
if (parsed.endpoint) parts.push(`endpoint: ${parsed.endpoint}`);
|
|
214359
|
-
if (parts.length > 0) return parts.join(" | ");
|
|
214360
|
-
} catch {
|
|
214361
|
-
}
|
|
214362
|
-
}
|
|
214363
|
-
const statusMatch = msg.match(/HTTP (\d+):/);
|
|
214364
|
-
if (statusMatch) {
|
|
214365
|
-
const code = parseInt(statusMatch[1], 10);
|
|
214366
|
-
if (code === 500) return "Internal server error \u2014 the API encountered an unexpected issue";
|
|
214367
|
-
if (code === 401) return "Authentication failed \u2014 check your API key";
|
|
214368
|
-
if (code === 403) return "Access denied \u2014 your API key lacks permission";
|
|
214369
|
-
if (code === 404) return "Not found \u2014 the resource does not exist";
|
|
214370
|
-
if (code === 429) return "Rate limited \u2014 too many requests, try again later";
|
|
214371
|
-
return `Server error (HTTP ${code})`;
|
|
214406
|
+
function isGitRepo() {
|
|
214407
|
+
return (0, import_node_fs2.existsSync)((0, import_node_path2.resolve)(process.cwd(), ".git"));
|
|
214408
|
+
}
|
|
214409
|
+
function getUncommittedDiff() {
|
|
214410
|
+
try {
|
|
214411
|
+
return (0, import_node_child_process.execSync)("git diff", { encoding: "utf8", cwd: process.cwd() }).trim();
|
|
214412
|
+
} catch {
|
|
214413
|
+
return "";
|
|
214372
214414
|
}
|
|
214373
|
-
return msg;
|
|
214374
214415
|
}
|
|
214375
|
-
function
|
|
214376
|
-
|
|
214377
|
-
|
|
214378
|
-
|
|
214379
|
-
|
|
214380
|
-
|
|
214381
|
-
const fn = isHttps ? import_node_https.request : import_node_http.request;
|
|
214382
|
-
const headers = {
|
|
214383
|
-
"Content-Type": "application/json",
|
|
214384
|
-
Accept: "application/json"
|
|
214385
|
-
};
|
|
214386
|
-
const apiKey = loadApiKey();
|
|
214387
|
-
if (apiKey) {
|
|
214388
|
-
headers["X-API-Key"] = apiKey;
|
|
214389
|
-
}
|
|
214390
|
-
const req = fn(
|
|
214391
|
-
fullUrl,
|
|
214392
|
-
{ method, headers },
|
|
214393
|
-
(res) => {
|
|
214394
|
-
let data = "";
|
|
214395
|
-
res.on("data", (chunk) => data += chunk.toString());
|
|
214396
|
-
res.on("end", () => {
|
|
214397
|
-
if (res.statusCode && res.statusCode >= 400) {
|
|
214398
|
-
reject(new Error(`HTTP ${res.statusCode}: ${data}`));
|
|
214399
|
-
return;
|
|
214400
|
-
}
|
|
214401
|
-
try {
|
|
214402
|
-
resolve9(JSON.parse(data));
|
|
214403
|
-
} catch {
|
|
214404
|
-
resolve9(data);
|
|
214405
|
-
}
|
|
214406
|
-
});
|
|
214407
|
-
}
|
|
214408
|
-
);
|
|
214409
|
-
req.on("error", (e) => reject(new Error(`Connection failed: ${e.message}`)));
|
|
214410
|
-
if (body) req.write(JSON.stringify(body));
|
|
214411
|
-
req.end();
|
|
214412
|
-
});
|
|
214416
|
+
function getStagedDiff() {
|
|
214417
|
+
try {
|
|
214418
|
+
return (0, import_node_child_process.execSync)("git diff --staged", { encoding: "utf8", cwd: process.cwd() }).trim();
|
|
214419
|
+
} catch {
|
|
214420
|
+
return "";
|
|
214421
|
+
}
|
|
214413
214422
|
}
|
|
214414
|
-
|
|
214415
|
-
|
|
214416
|
-
|
|
214417
|
-
|
|
214418
|
-
|
|
214419
|
-
|
|
214420
|
-
};
|
|
214421
|
-
function getApiUrl() {
|
|
214422
|
-
return getBaseUrl();
|
|
214423
|
+
function getLastCommitHash() {
|
|
214424
|
+
try {
|
|
214425
|
+
return (0, import_node_child_process.execSync)("git rev-parse HEAD", { encoding: "utf8", cwd: process.cwd() }).trim();
|
|
214426
|
+
} catch {
|
|
214427
|
+
return "unknown";
|
|
214428
|
+
}
|
|
214423
214429
|
}
|
|
214424
|
-
function
|
|
214425
|
-
|
|
214430
|
+
function getChangedFilesList() {
|
|
214431
|
+
try {
|
|
214432
|
+
const unstaged = (0, import_node_child_process.execSync)("git diff --name-only HEAD", { encoding: "utf8", cwd: process.cwd() }).trim();
|
|
214433
|
+
const staged = (0, import_node_child_process.execSync)("git diff --staged --name-only", { encoding: "utf8", cwd: process.cwd() }).trim();
|
|
214434
|
+
const files = /* @__PURE__ */ new Set();
|
|
214435
|
+
if (unstaged) unstaged.split("\n").forEach((f) => files.add(f));
|
|
214436
|
+
if (staged) staged.split("\n").forEach((f) => files.add(f));
|
|
214437
|
+
return [...files].filter(Boolean);
|
|
214438
|
+
} catch {
|
|
214439
|
+
return [];
|
|
214440
|
+
}
|
|
214426
214441
|
}
|
|
214427
|
-
function
|
|
214428
|
-
|
|
214442
|
+
function getAuthorName() {
|
|
214443
|
+
try {
|
|
214444
|
+
return (0, import_node_child_process.execSync)("git config user.name", { encoding: "utf8", cwd: process.cwd() }).trim();
|
|
214445
|
+
} catch {
|
|
214446
|
+
return "unknown";
|
|
214447
|
+
}
|
|
214429
214448
|
}
|
|
214430
214449
|
|
|
214431
214450
|
// src/commands/init.ts
|
|
214432
|
-
init_git();
|
|
214433
214451
|
var import_scanner = __toESM(require_dist());
|
|
214434
214452
|
var import_node_fs4 = require("node:fs");
|
|
214435
214453
|
var import_node_path4 = require("node:path");
|
|
214454
|
+
init_analytics();
|
|
214436
214455
|
|
|
214437
214456
|
// src/commands/install-skill.ts
|
|
214438
214457
|
var import_node_fs3 = require("node:fs");
|
|
@@ -214533,13 +214552,22 @@ async function installSkillCommand() {
|
|
|
214533
214552
|
const icon = (s) => s === "created" ? "created \u2713" : s === "updated" ? "updated \u2713" : "already configured \u2713";
|
|
214534
214553
|
console.log(` CLAUDE.md ${icon(result.claudeMd)}`);
|
|
214535
214554
|
console.log(` AGENTS.md ${icon(result.agentsMd)}`);
|
|
214555
|
+
try {
|
|
214556
|
+
const { api: api2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
|
|
214557
|
+
const me = await api2.get("/me");
|
|
214558
|
+
if (me?.slug) {
|
|
214559
|
+
const { analyticsEvent: analyticsEvent2 } = await Promise.resolve().then(() => (init_analytics(), analytics_exports));
|
|
214560
|
+
analyticsEvent2("cli_install_skill", me.slug, { claude_md: result.claudeMd, agents_md: result.agentsMd });
|
|
214561
|
+
}
|
|
214562
|
+
} catch {
|
|
214563
|
+
}
|
|
214536
214564
|
return result;
|
|
214537
214565
|
}
|
|
214538
214566
|
|
|
214539
214567
|
// src/commands/init.ts
|
|
214540
214568
|
function getVersion() {
|
|
214541
214569
|
try {
|
|
214542
|
-
return "0.1.
|
|
214570
|
+
return "0.1.34";
|
|
214543
214571
|
} catch {
|
|
214544
214572
|
return "0.0.0";
|
|
214545
214573
|
}
|
|
@@ -214846,13 +214874,17 @@ async function initCommand(args) {
|
|
|
214846
214874
|
console.log("");
|
|
214847
214875
|
console.log(DIVIDER);
|
|
214848
214876
|
console.log("");
|
|
214877
|
+
if (!isGitInstalled()) {
|
|
214878
|
+
console.error("Error: git is not installed. Install git and try again.");
|
|
214879
|
+
process.exit(1);
|
|
214880
|
+
}
|
|
214849
214881
|
if (!isGitRepo()) {
|
|
214850
|
-
console.error("Error: not a git repository.");
|
|
214882
|
+
console.error("Error: not a git repository. Run `git init` first.");
|
|
214851
214883
|
process.exit(1);
|
|
214852
214884
|
}
|
|
214853
214885
|
const remote = getGitRemote();
|
|
214854
214886
|
if (!remote) {
|
|
214855
|
-
console.error("Error: no git remote
|
|
214887
|
+
console.error("Error: no git remote configured. Run `git remote add origin <url>` first.");
|
|
214856
214888
|
process.exit(1);
|
|
214857
214889
|
}
|
|
214858
214890
|
let workspace;
|
|
@@ -214975,7 +215007,8 @@ async function initCommand(args) {
|
|
|
214975
215007
|
gotchas: watcher.gotchas,
|
|
214976
215008
|
ownership: watcher.ownership,
|
|
214977
215009
|
where_repos: [repo.id],
|
|
214978
|
-
created_by: "driftless-init"
|
|
215010
|
+
created_by: "driftless-init",
|
|
215011
|
+
suggested: true
|
|
214979
215012
|
});
|
|
214980
215013
|
watchersCreated++;
|
|
214981
215014
|
} catch {
|
|
@@ -215006,11 +215039,18 @@ async function initCommand(args) {
|
|
|
215006
215039
|
console.log(" next \u2192 driftless context list");
|
|
215007
215040
|
console.log("");
|
|
215008
215041
|
console.log(DIVIDER);
|
|
215042
|
+
analyticsEvent("cli_init_run", workspaceSlug, {
|
|
215043
|
+
framework: summary.framework,
|
|
215044
|
+
endpoints: summary.endpoints ?? components.filter((c) => c.type === "controller").length,
|
|
215045
|
+
components: components.length,
|
|
215046
|
+
watchers_created: watchersCreated
|
|
215047
|
+
});
|
|
215009
215048
|
process.exit(0);
|
|
215010
215049
|
}
|
|
215011
215050
|
|
|
215012
215051
|
// src/commands/scan.ts
|
|
215013
|
-
|
|
215052
|
+
init_api_client();
|
|
215053
|
+
init_analytics();
|
|
215014
215054
|
function emitJSON(data) {
|
|
215015
215055
|
console.log(JSON.stringify(data, null, 2));
|
|
215016
215056
|
}
|
|
@@ -215086,6 +215126,7 @@ async function scanCommand(args) {
|
|
|
215086
215126
|
});
|
|
215087
215127
|
const rulesEvaluated = result.rules_evaluated || 0;
|
|
215088
215128
|
if (result.status === "clean") {
|
|
215129
|
+
analyticsEvent("cli_scan_run", workspace.slug, { violations_found: false, count: 0 });
|
|
215089
215130
|
if (isJSON) {
|
|
215090
215131
|
emitJSON({ status: "clean", files: changedFiles, rules_evaluated: rulesEvaluated, violations: [] });
|
|
215091
215132
|
} else {
|
|
@@ -215094,6 +215135,7 @@ async function scanCommand(args) {
|
|
|
215094
215135
|
process.exit(0);
|
|
215095
215136
|
}
|
|
215096
215137
|
if (!result.violations || result.violations.length === 0) {
|
|
215138
|
+
analyticsEvent("cli_scan_run", workspace.slug, { violations_found: false, count: 0 });
|
|
215097
215139
|
if (isJSON) {
|
|
215098
215140
|
emitJSON({ status: "clean", files: changedFiles, rules_evaluated: rulesEvaluated, violations: [] });
|
|
215099
215141
|
} else {
|
|
@@ -215101,6 +215143,7 @@ async function scanCommand(args) {
|
|
|
215101
215143
|
}
|
|
215102
215144
|
process.exit(0);
|
|
215103
215145
|
}
|
|
215146
|
+
analyticsEvent("cli_scan_run", workspace.slug, { violations_found: true, count: result.violations.length });
|
|
215104
215147
|
if (isJSON) {
|
|
215105
215148
|
emitJSON({ status: "violated", files: changedFiles, rules_evaluated: rulesEvaluated, violations: result.violations });
|
|
215106
215149
|
} else {
|
|
@@ -215121,9 +215164,10 @@ ${result.violations.length} violation(s) found (${rulesEvaluated} rule(s) evalua
|
|
|
215121
215164
|
}
|
|
215122
215165
|
|
|
215123
215166
|
// src/commands/context.ts
|
|
215124
|
-
|
|
215167
|
+
init_api_client();
|
|
215125
215168
|
var import_node_fs5 = require("node:fs");
|
|
215126
215169
|
var import_node_path5 = require("node:path");
|
|
215170
|
+
init_analytics();
|
|
215127
215171
|
function parseArgs(args) {
|
|
215128
215172
|
const flags = {};
|
|
215129
215173
|
const positional = [];
|
|
@@ -215314,14 +215358,20 @@ async function contextCommand(args) {
|
|
|
215314
215358
|
if (flags["auto"]) query.push("auto=true");
|
|
215315
215359
|
if (flags["manual"]) query.push("manual=true");
|
|
215316
215360
|
if (flags["repo"]) query.push(`repo=${encodeURIComponent(flags["repo"])}`);
|
|
215361
|
+
if (flags["suggested"]) query.push("suggested=true");
|
|
215362
|
+
else query.push("suggested=false");
|
|
215317
215363
|
const qs = query.length > 0 ? `?${query.join("&")}` : "";
|
|
215318
215364
|
try {
|
|
215319
215365
|
const summaries = await api.get(`/workspaces/${workspaceSlug}/watchers${qs}`);
|
|
215320
215366
|
if (summaries.length === 0 && !isJSON) {
|
|
215321
|
-
|
|
215322
|
-
|
|
215323
|
-
|
|
215324
|
-
|
|
215367
|
+
if (flags["suggested"]) {
|
|
215368
|
+
console.log("No suggested topics. Run `driftless init` to generate suggestions.");
|
|
215369
|
+
} else {
|
|
215370
|
+
console.log("No context topics yet.");
|
|
215371
|
+
console.log("\nNext:");
|
|
215372
|
+
console.log(' driftless context add "auth-boundaries" --what "..." --how "..." --pattern "src/auth/**"');
|
|
215373
|
+
console.log(" driftless init # generate topic suggestions from your codebase");
|
|
215374
|
+
}
|
|
215325
215375
|
}
|
|
215326
215376
|
if (isJSON) {
|
|
215327
215377
|
emitJSON2(summaries);
|
|
@@ -215388,6 +215438,7 @@ async function contextCommand(args) {
|
|
|
215388
215438
|
}
|
|
215389
215439
|
try {
|
|
215390
215440
|
const ctx = await api.get(`/workspaces/${workspaceSlug}/watchers/${slug}`);
|
|
215441
|
+
analyticsEvent("cli_context_get", workspaceSlug, { topic: slug });
|
|
215391
215442
|
if (isJSON) {
|
|
215392
215443
|
const sanitized = JSON.parse(JSON.stringify(ctx, (_key, value) => {
|
|
215393
215444
|
if (typeof value === "string" && value.length > 500) return value.slice(0, 500) + "\u2026";
|
|
@@ -215461,6 +215512,7 @@ async function contextCommand(args) {
|
|
|
215461
215512
|
ownership: flags["ownership"],
|
|
215462
215513
|
file_content: fileContent
|
|
215463
215514
|
});
|
|
215515
|
+
analyticsEvent("context_created", workspaceSlug, { topic: name, has_pattern: !!pattern, has_what: !!flags["what"] });
|
|
215464
215516
|
if (isJSON) {
|
|
215465
215517
|
emitJSON2(result);
|
|
215466
215518
|
} else {
|
|
@@ -215514,12 +215566,22 @@ async function contextCommand(args) {
|
|
|
215514
215566
|
console.error('PR 3: --gotcha, --invariant, --check, --enforce "<rule-description>"');
|
|
215515
215567
|
process.exit(1);
|
|
215516
215568
|
}
|
|
215569
|
+
const currentRemote = getGitRemote();
|
|
215570
|
+
if (currentRemote) {
|
|
215571
|
+
try {
|
|
215572
|
+
const repos = await api.get(`/workspaces/${workspaceSlug}/repos`);
|
|
215573
|
+
const match = repos.find((r) => r.github_org === currentRemote.org && r.github_repo === currentRemote.repo);
|
|
215574
|
+
if (match) updates._add_repo = match.id;
|
|
215575
|
+
} catch {
|
|
215576
|
+
}
|
|
215577
|
+
}
|
|
215517
215578
|
if (flags["dry-run"]) {
|
|
215518
215579
|
console.log(`Would update topic '${slug}':`);
|
|
215519
215580
|
for (const [key, value] of Object.entries(updates)) {
|
|
215520
215581
|
if (key.startsWith("_")) continue;
|
|
215521
215582
|
console.log(` ${key}: ${value}`);
|
|
215522
215583
|
}
|
|
215584
|
+
if (updates._add_repo) console.log(` _add_repo: ${updates._add_repo} (${currentRemote?.org}/${currentRemote?.repo})`);
|
|
215523
215585
|
if (!isJSON) {
|
|
215524
215586
|
console.log("\n(Dry run \u2014 no changes written to Driftless Cloud)");
|
|
215525
215587
|
}
|
|
@@ -215533,6 +215595,7 @@ async function contextCommand(args) {
|
|
|
215533
215595
|
`/workspaces/${workspaceSlug}/watchers/${slug}`,
|
|
215534
215596
|
updates
|
|
215535
215597
|
);
|
|
215598
|
+
analyticsEvent("cli_context_update", workspaceSlug, { topic: slug });
|
|
215536
215599
|
if (isJSON) {
|
|
215537
215600
|
emitJSON2(result);
|
|
215538
215601
|
} else {
|
|
@@ -215887,13 +215950,10 @@ Done: ${created} created, ${updated} updated.`);
|
|
|
215887
215950
|
Run 'driftless help context' for full reference.`);
|
|
215888
215951
|
}
|
|
215889
215952
|
|
|
215890
|
-
// src/commands/
|
|
215891
|
-
|
|
215892
|
-
var import_node_fs6 = require("node:fs");
|
|
215893
|
-
var import_node_path6 = require("node:path");
|
|
215953
|
+
// src/commands/sync.ts
|
|
215954
|
+
init_api_client();
|
|
215894
215955
|
function parseArgs2(args) {
|
|
215895
215956
|
const flags = {};
|
|
215896
|
-
const positional = [];
|
|
215897
215957
|
for (let i = 0; i < args.length; i++) {
|
|
215898
215958
|
if (args[i].startsWith("--")) {
|
|
215899
215959
|
const key = args[i].slice(2);
|
|
@@ -215901,14 +215961,10 @@ function parseArgs2(args) {
|
|
|
215901
215961
|
if (next && !next.startsWith("--")) {
|
|
215902
215962
|
flags[key] = next;
|
|
215903
215963
|
i++;
|
|
215904
|
-
} else
|
|
215905
|
-
flags[key] = true;
|
|
215906
|
-
}
|
|
215907
|
-
} else {
|
|
215908
|
-
positional.push(args[i]);
|
|
215964
|
+
} else flags[key] = true;
|
|
215909
215965
|
}
|
|
215910
215966
|
}
|
|
215911
|
-
return { flags
|
|
215967
|
+
return { flags };
|
|
215912
215968
|
}
|
|
215913
215969
|
function emitJSON3(data) {
|
|
215914
215970
|
console.log(JSON.stringify(data, null, 2));
|
|
@@ -215926,250 +215982,115 @@ async function resolveWorkspaceSlug2() {
|
|
|
215926
215982
|
}
|
|
215927
215983
|
return remote.org;
|
|
215928
215984
|
}
|
|
215929
|
-
async function
|
|
215985
|
+
async function syncCommand(args) {
|
|
215930
215986
|
if (!isGitRepo()) {
|
|
215931
215987
|
console.error("Error: not a git repository.");
|
|
215932
215988
|
process.exit(1);
|
|
215933
215989
|
}
|
|
215934
|
-
const
|
|
215935
|
-
const { flags, positional } = parseArgs2(args);
|
|
215936
|
-
const subCommand = positional[0];
|
|
215990
|
+
const { flags } = parseArgs2(args);
|
|
215937
215991
|
const isJSON = !!flags["json"];
|
|
215938
|
-
|
|
215939
|
-
|
|
215940
|
-
|
|
215941
|
-
|
|
215942
|
-
|
|
215992
|
+
const remote = getGitRemote();
|
|
215993
|
+
if (!remote) {
|
|
215994
|
+
console.error("Error: no git remote configured.");
|
|
215995
|
+
process.exit(1);
|
|
215996
|
+
}
|
|
215997
|
+
const workspaceSlug = await resolveWorkspaceSlug2();
|
|
215998
|
+
let repoId = null;
|
|
215999
|
+
try {
|
|
216000
|
+
const repos = await api.get(`/workspaces/${workspaceSlug}/repos`);
|
|
216001
|
+
const match = repos.find((r) => r.github_org === remote.org && r.github_repo === remote.repo);
|
|
216002
|
+
if (match) repoId = match.id;
|
|
216003
|
+
} catch {
|
|
216004
|
+
}
|
|
216005
|
+
if (!repoId) {
|
|
216006
|
+
if (!isJSON) {
|
|
216007
|
+
console.log(`Repo '${remote.org}/${remote.repo}' not found in workspace.`);
|
|
216008
|
+
console.log("Run driftless init to register this repo first.");
|
|
215943
216009
|
} else {
|
|
215944
|
-
|
|
216010
|
+
emitJSON3({ error: "repo_not_found", repo: `${remote.org}/${remote.repo}` });
|
|
215945
216011
|
}
|
|
215946
|
-
|
|
215947
|
-
|
|
215948
|
-
|
|
215949
|
-
|
|
215950
|
-
|
|
215951
|
-
|
|
215952
|
-
|
|
215953
|
-
|
|
215954
|
-
|
|
215955
|
-
|
|
215956
|
-
|
|
215957
|
-
|
|
215958
|
-
|
|
215959
|
-
|
|
215960
|
-
|
|
215961
|
-
|
|
215962
|
-
|
|
215963
|
-
)
|
|
215964
|
-
|
|
215965
|
-
|
|
215966
|
-
|
|
215967
|
-
|
|
215968
|
-
|
|
215969
|
-
|
|
215970
|
-
|
|
215971
|
-
|
|
215972
|
-
|
|
215973
|
-
|
|
215974
|
-
|
|
215975
|
-
|
|
215976
|
-
|
|
215977
|
-
|
|
215978
|
-
|
|
215979
|
-
|
|
215980
|
-
}
|
|
215981
|
-
if (ctx.rules) {
|
|
215982
|
-
for (const rule of ctx.rules) {
|
|
215983
|
-
rules.push(rule.name);
|
|
215984
|
-
}
|
|
215985
|
-
}
|
|
215986
|
-
}
|
|
215987
|
-
if (isJSON) {
|
|
215988
|
-
emitJSON3({
|
|
215989
|
-
files,
|
|
215990
|
-
missing_files: missingFiles,
|
|
215991
|
-
topics,
|
|
215992
|
-
docs: [...new Set(docs)],
|
|
215993
|
-
rules,
|
|
215994
|
-
gotchas,
|
|
215995
|
-
context_summary: {
|
|
215996
|
-
what: allWhat,
|
|
215997
|
-
how: allHow
|
|
215998
|
-
}
|
|
215999
|
-
});
|
|
216000
|
-
} else {
|
|
216001
|
-
console.log(`Session started for ${files.length} file(s):
|
|
216012
|
+
process.exit(0);
|
|
216013
|
+
}
|
|
216014
|
+
const [eventsRes, staleTopics, violations, suggestedTopics] = await Promise.allSettled([
|
|
216015
|
+
api.get(`/workspaces/${workspaceSlug}/watchers/events?repo_id=${repoId}&limit=20`),
|
|
216016
|
+
api.get(`/workspaces/${workspaceSlug}/watchers?stale=true&repo=${repoId}`),
|
|
216017
|
+
api.get(`/workspaces/${workspaceSlug}/violations?repo_id=${repoId}&status=open&limit=20`),
|
|
216018
|
+
api.get(`/workspaces/${workspaceSlug}/watchers?suggested=true`)
|
|
216019
|
+
]);
|
|
216020
|
+
const events = eventsRes.status === "fulfilled" ? eventsRes.value.events ?? [] : [];
|
|
216021
|
+
const stale = staleTopics.status === "fulfilled" ? staleTopics.value : [];
|
|
216022
|
+
const rawViolations = violations.status === "fulfilled" ? violations.value : [];
|
|
216023
|
+
const openViolations = Array.isArray(rawViolations) ? rawViolations : rawViolations.violations ?? rawViolations.items ?? [];
|
|
216024
|
+
const suggested = suggestedTopics.status === "fulfilled" ? suggestedTopics.value : [];
|
|
216025
|
+
if (isJSON) {
|
|
216026
|
+
emitJSON3({
|
|
216027
|
+
repo: `${remote.org}/${remote.repo}`,
|
|
216028
|
+
stale_topics: stale.map((t) => ({ topic: t.topic, reason: t.stale?.reason ?? null })),
|
|
216029
|
+
recent_events: events.slice(0, 10).map((e) => ({
|
|
216030
|
+
type: e.event_type,
|
|
216031
|
+
topic: e.watcher_slug,
|
|
216032
|
+
detail: e.detail,
|
|
216033
|
+
created_at: e.created_at
|
|
216034
|
+
})),
|
|
216035
|
+
open_violations: openViolations.map((v) => ({
|
|
216036
|
+
id: v.id,
|
|
216037
|
+
rule_id: v.rule_id,
|
|
216038
|
+
status: v.status,
|
|
216039
|
+
author: v.author
|
|
216040
|
+
})),
|
|
216041
|
+
suggested_pending: suggested.length
|
|
216042
|
+
});
|
|
216043
|
+
process.exit(0);
|
|
216044
|
+
}
|
|
216045
|
+
console.log(`\u258C ${remote.org}/${remote.repo}
|
|
216002
216046
|
`);
|
|
216003
|
-
|
|
216004
|
-
|
|
216005
|
-
|
|
216006
|
-
|
|
216007
|
-
|
|
216008
|
-
|
|
216009
|
-
Warning: ${missingFiles.length} file(s) not found locally.`);
|
|
216010
|
-
}
|
|
216011
|
-
if (topics.length > 0) {
|
|
216012
|
-
console.log(`
|
|
216013
|
-
Matched ${topics.length} context topic(s):`);
|
|
216014
|
-
for (const r of results) {
|
|
216015
|
-
console.log(` \u258C ${r.context.topic} (${r.match_reason})`);
|
|
216016
|
-
if (r.context.description.what) {
|
|
216017
|
-
console.log(` ${r.context.description.what}`);
|
|
216018
|
-
}
|
|
216019
|
-
}
|
|
216020
|
-
} else {
|
|
216021
|
-
console.log("\nNo context topics match these files.");
|
|
216022
|
-
}
|
|
216023
|
-
if (docs.length > 0) {
|
|
216024
|
-
console.log(`
|
|
216025
|
-
Anchored docs:`);
|
|
216026
|
-
for (const d of [...new Set(docs)]) {
|
|
216027
|
-
console.log(` \u{1F4C4} ${d}`);
|
|
216028
|
-
}
|
|
216029
|
-
}
|
|
216030
|
-
if (gotchas.length > 0) {
|
|
216031
|
-
console.log(`
|
|
216032
|
-
Gotchas:`);
|
|
216033
|
-
for (const g of gotchas) {
|
|
216034
|
-
console.log(` ! ${g}`);
|
|
216035
|
-
}
|
|
216036
|
-
}
|
|
216037
|
-
if (rules.length > 0) {
|
|
216038
|
-
console.log(`
|
|
216039
|
-
Rules that will be evaluated:`);
|
|
216040
|
-
for (const r of rules) {
|
|
216041
|
-
console.log(` \u2713 ${r}`);
|
|
216042
|
-
}
|
|
216043
|
-
}
|
|
216044
|
-
console.log("\nBefore finishing:");
|
|
216045
|
-
console.log(" driftless scan --diff");
|
|
216046
|
-
console.log(" driftless session end");
|
|
216047
|
-
}
|
|
216048
|
-
} catch (e) {
|
|
216049
|
-
console.error(`Session start failed: ${formatError(e)}`);
|
|
216050
|
-
process.exit(1);
|
|
216047
|
+
if (stale.length > 0) {
|
|
216048
|
+
console.log(`\u26A0 ${stale.length} stale topic${stale.length === 1 ? "" : "s"} \u2014 code changed, context not updated:`);
|
|
216049
|
+
for (const t of stale) {
|
|
216050
|
+
console.log(` ${t.topic}`);
|
|
216051
|
+
if (t.stale?.reason) console.log(` reason: ${t.stale.reason}`);
|
|
216052
|
+
console.log(` \u2192 driftless context update ${t.topic} --what "..." --how "..."`);
|
|
216051
216053
|
}
|
|
216052
|
-
|
|
216054
|
+
console.log("");
|
|
216053
216055
|
}
|
|
216054
|
-
if (
|
|
216055
|
-
|
|
216056
|
-
|
|
216057
|
-
|
|
216058
|
-
|
|
216056
|
+
if (events.length > 0) {
|
|
216057
|
+
console.log(`Recent activity (${events.length}):`);
|
|
216058
|
+
for (const e of events.slice(0, 10)) {
|
|
216059
|
+
const topic = e.watcher_slug ? `[${e.watcher_slug}]` : "";
|
|
216060
|
+
const detail = e.detail ? ` ${e.detail.slice(0, 80)}` : "";
|
|
216061
|
+
console.log(` ${e.event_type.padEnd(14)} ${topic}${detail}`);
|
|
216059
216062
|
}
|
|
216060
|
-
|
|
216061
|
-
|
|
216062
|
-
|
|
216063
|
-
|
|
216064
|
-
|
|
216065
|
-
}
|
|
216066
|
-
const me = await api.get("/me");
|
|
216067
|
-
const repos = await api.get(`/workspaces/${me.slug}/repos`);
|
|
216068
|
-
const repo = repos.find((r) => r.github_org === remote.org && r.github_repo === remote.repo);
|
|
216069
|
-
if (!repo) {
|
|
216070
|
-
console.error(`Repo '${remote.repo}' not found. Run 'driftless init' first.`);
|
|
216071
|
-
process.exit(1);
|
|
216072
|
-
}
|
|
216073
|
-
const { getUncommittedDiff: getUncommittedDiff2, getStagedDiff: getStagedDiff2, getLastCommitHash: getLastCommitHash2, getAuthorName: getAuthorName2 } = await Promise.resolve().then(() => (init_git(), git_exports));
|
|
216074
|
-
const staged = getStagedDiff2();
|
|
216075
|
-
const unstaged = getUncommittedDiff2();
|
|
216076
|
-
const diff = [staged, unstaged].filter(Boolean).join("\n");
|
|
216077
|
-
if (!diff && !isJSON) {
|
|
216078
|
-
console.log("No changes to scan. Session clean.");
|
|
216079
|
-
process.exit(0);
|
|
216080
|
-
}
|
|
216081
|
-
const scanResult = await api.post("/scan", {
|
|
216082
|
-
workspace_id: me.workspace_id,
|
|
216083
|
-
repo_id: repo.id,
|
|
216084
|
-
diff,
|
|
216085
|
-
commit_hash: getLastCommitHash2(),
|
|
216086
|
-
author: getAuthorName2()
|
|
216087
|
-
});
|
|
216088
|
-
const rulesEvaluated = scanResult.rules_evaluated || 0;
|
|
216089
|
-
const violations = scanResult.violations || [];
|
|
216090
|
-
const contextResults = files.length > 0 ? await api.post(
|
|
216091
|
-
`/workspaces/${me.slug}/watchers/match-files`,
|
|
216092
|
-
{ files }
|
|
216093
|
-
) : [];
|
|
216094
|
-
const staleTopics = contextResults.filter((r) => r.context.stale?.is_stale);
|
|
216095
|
-
if (isJSON) {
|
|
216096
|
-
emitJSON3({
|
|
216097
|
-
files,
|
|
216098
|
-
rules_evaluated: rulesEvaluated,
|
|
216099
|
-
violations,
|
|
216100
|
-
stale_topics: staleTopics.map((r) => ({ topic: r.context.topic, reason: r.context.stale.reason })),
|
|
216101
|
-
context_matched: contextResults.map((r) => r.context.topic)
|
|
216102
|
-
});
|
|
216103
|
-
} else {
|
|
216104
|
-
console.log(`Session end \u2014 ${files.length} file(s) changed:
|
|
216105
|
-
`);
|
|
216106
|
-
for (const f of files) {
|
|
216107
|
-
console.log(` \u270E ${f}`);
|
|
216108
|
-
}
|
|
216109
|
-
console.log(`
|
|
216110
|
-
${rulesEvaluated} rule(s) evaluated.`);
|
|
216111
|
-
if (violations.length > 0) {
|
|
216112
|
-
console.log(`
|
|
216113
|
-
${violations.length} violation(s) found:`);
|
|
216114
|
-
for (const v of violations) {
|
|
216115
|
-
console.log(` \u2717 [${v.severity.toUpperCase()}] ${v.rule_name}`);
|
|
216116
|
-
console.log(` ${v.file_path}:${v.line_number} \u2014 ${v.explanation}`);
|
|
216117
|
-
}
|
|
216118
|
-
console.log("\nFix violations before pushing.");
|
|
216119
|
-
} else {
|
|
216120
|
-
console.log("No violations.");
|
|
216121
|
-
}
|
|
216122
|
-
if (staleTopics.length > 0) {
|
|
216123
|
-
console.log(`
|
|
216124
|
-
\u26A0 ${staleTopics.length} stale context topic(s):`);
|
|
216125
|
-
for (const s of staleTopics) {
|
|
216126
|
-
console.log(` ${s.context.topic} \u2014 ${s.context.stale.reason}`);
|
|
216127
|
-
console.log(` \u2192 driftless context update ${s.context.topic} --what "..." --how "..."`);
|
|
216128
|
-
}
|
|
216129
|
-
}
|
|
216130
|
-
if (contextResults.length > 0) {
|
|
216131
|
-
console.log(`
|
|
216132
|
-
Context touched:`);
|
|
216133
|
-
for (const r of contextResults) {
|
|
216134
|
-
console.log(` \u258C ${r.context.topic}`);
|
|
216135
|
-
}
|
|
216136
|
-
}
|
|
216137
|
-
console.log("\nLearned something? Update context:");
|
|
216138
|
-
console.log(' driftless context update <topic> --gotchas "..."');
|
|
216139
|
-
console.log(' driftless context add <new-topic> --what "..." --pattern "..."');
|
|
216140
|
-
}
|
|
216141
|
-
if (violations.length > 0) {
|
|
216142
|
-
process.exit(1);
|
|
216143
|
-
}
|
|
216144
|
-
} catch (e) {
|
|
216145
|
-
console.error(`Session end failed: ${formatError(e)}`);
|
|
216146
|
-
process.exit(1);
|
|
216063
|
+
console.log("");
|
|
216064
|
+
}
|
|
216065
|
+
if (openViolations.length > 0) {
|
|
216066
|
+
console.log(`\u2717 ${openViolations.length} open violation${openViolations.length === 1 ? "" : "s"}:`);
|
|
216067
|
+
for (const v of openViolations.slice(0, 5)) {
|
|
216068
|
+
console.log(` ${v.rule_id} ${v.author ? `(${v.author})` : ""}`);
|
|
216147
216069
|
}
|
|
216148
|
-
|
|
216070
|
+
if (openViolations.length > 5) console.log(` ... and ${openViolations.length - 5} more`);
|
|
216071
|
+
console.log("");
|
|
216149
216072
|
}
|
|
216150
|
-
|
|
216151
|
-
|
|
216152
|
-
|
|
216153
|
-
|
|
216154
|
-
|
|
216155
|
-
|
|
216156
|
-
|
|
216157
|
-
|
|
216158
|
-
|
|
216159
|
-
|
|
216160
|
-
|
|
216161
|
-
driftless session start # uses local changes
|
|
216162
|
-
driftless session end # scan + context report`);
|
|
216073
|
+
if (suggested.length > 0) {
|
|
216074
|
+
console.log(`${suggested.length} suggested topic${suggested.length === 1 ? "" : "s"} from init \u2014 review and confirm:`);
|
|
216075
|
+
console.log(` driftless context list --suggested`);
|
|
216076
|
+
console.log("");
|
|
216077
|
+
}
|
|
216078
|
+
if (stale.length === 0 && events.length === 0 && openViolations.length === 0 && suggested.length === 0) {
|
|
216079
|
+
console.log("Cloud context is up to date. Nothing to sync.");
|
|
216080
|
+
} else {
|
|
216081
|
+
console.log("Review stale topics, then update context before touching code.");
|
|
216082
|
+
}
|
|
216083
|
+
process.exit(0);
|
|
216163
216084
|
}
|
|
216164
216085
|
|
|
216165
216086
|
// src/commands/login.ts
|
|
216166
|
-
var
|
|
216167
|
-
var
|
|
216087
|
+
var import_node_fs6 = require("node:fs");
|
|
216088
|
+
var import_node_path6 = require("node:path");
|
|
216168
216089
|
var import_node_readline = require("node:readline");
|
|
216169
216090
|
var import_node_child_process2 = require("node:child_process");
|
|
216170
216091
|
var import_node_os2 = require("node:os");
|
|
216171
|
-
var CONFIG_DIR = (0,
|
|
216172
|
-
var CONFIG_PATH2 = (0,
|
|
216092
|
+
var CONFIG_DIR = (0, import_node_path6.resolve)((0, import_node_os2.homedir)(), ".driftless");
|
|
216093
|
+
var CONFIG_PATH2 = (0, import_node_path6.resolve)(CONFIG_DIR, "config.json");
|
|
216173
216094
|
function openBrowser(url) {
|
|
216174
216095
|
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
216175
216096
|
const child = (0, import_node_child_process2.spawn)(cmd, [url], { stdio: "ignore", detached: true });
|
|
@@ -216199,10 +216120,10 @@ async function loginCommand(args) {
|
|
|
216199
216120
|
input: process.stdin,
|
|
216200
216121
|
output: process.stdout
|
|
216201
216122
|
});
|
|
216202
|
-
const apiKey = await new Promise((
|
|
216123
|
+
const apiKey = await new Promise((resolve8) => {
|
|
216203
216124
|
rl.question("Paste your API key: ", (answer) => {
|
|
216204
216125
|
rl.close();
|
|
216205
|
-
|
|
216126
|
+
resolve8(answer.trim());
|
|
216206
216127
|
});
|
|
216207
216128
|
});
|
|
216208
216129
|
if (!apiKey.startsWith("drift_")) {
|
|
@@ -216215,10 +216136,10 @@ async function loginCommand(args) {
|
|
|
216215
216136
|
function saveConfig(apiKey, apiUrl) {
|
|
216216
216137
|
const url = apiUrl || "https://api.driftless.icu/api/v1";
|
|
216217
216138
|
try {
|
|
216218
|
-
if (!(0,
|
|
216219
|
-
(0,
|
|
216139
|
+
if (!(0, import_node_fs6.existsSync)(CONFIG_DIR)) {
|
|
216140
|
+
(0, import_node_fs6.mkdirSync)(CONFIG_DIR, { recursive: true });
|
|
216220
216141
|
}
|
|
216221
|
-
(0,
|
|
216142
|
+
(0, import_node_fs6.writeFileSync)(
|
|
216222
216143
|
CONFIG_PATH2,
|
|
216223
216144
|
JSON.stringify({ api_key: apiKey, api_url: url }, null, 2) + "\n"
|
|
216224
216145
|
);
|
|
@@ -216235,9 +216156,9 @@ function saveConfig(apiKey, apiUrl) {
|
|
|
216235
216156
|
}
|
|
216236
216157
|
|
|
216237
216158
|
// src/commands/doctor.ts
|
|
216238
|
-
|
|
216239
|
-
var
|
|
216240
|
-
var
|
|
216159
|
+
init_api_client();
|
|
216160
|
+
var import_node_fs7 = require("node:fs");
|
|
216161
|
+
var import_node_path7 = require("node:path");
|
|
216241
216162
|
async function doctorCommand() {
|
|
216242
216163
|
const checks = [];
|
|
216243
216164
|
const apiKey = getApiKey();
|
|
@@ -216323,9 +216244,9 @@ async function doctorCommand() {
|
|
|
216323
216244
|
} else {
|
|
216324
216245
|
checks.push({ name: "Baseline", status: "warn", detail: "Skipped (no git remote)" });
|
|
216325
216246
|
}
|
|
216326
|
-
const agentsPath = (0,
|
|
216327
|
-
if ((0,
|
|
216328
|
-
const content = (0,
|
|
216247
|
+
const agentsPath = (0, import_node_path7.resolve)(process.cwd(), "AGENTS.md");
|
|
216248
|
+
if ((0, import_node_fs7.existsSync)(agentsPath)) {
|
|
216249
|
+
const content = (0, import_node_fs7.readFileSync)(agentsPath, "utf-8");
|
|
216329
216250
|
if (content.includes("driftless")) {
|
|
216330
216251
|
checks.push({ name: "AGENTS.md", status: "ok", detail: "Driftless skill installed" });
|
|
216331
216252
|
} else {
|
|
@@ -216381,7 +216302,7 @@ function pad2(s, n) {
|
|
|
216381
216302
|
}
|
|
216382
216303
|
|
|
216383
216304
|
// src/index.ts
|
|
216384
|
-
var VERSION = "0.1.
|
|
216305
|
+
var VERSION = "0.1.34";
|
|
216385
216306
|
var HELP_TEXT = `Driftless CLI v${VERSION} \u2014 Living repo context for humans and coding agents
|
|
216386
216307
|
|
|
216387
216308
|
Install: npm install -g @driftless-sh/cli
|
|
@@ -216389,8 +216310,7 @@ Docs: https://driftless.icu/docs
|
|
|
216389
216310
|
API: https://api.driftless.icu/api/v1
|
|
216390
216311
|
|
|
216391
216312
|
Agent loop:
|
|
216392
|
-
driftless
|
|
216393
|
-
driftless session end Scan changes + context report
|
|
216313
|
+
driftless sync Load context + scan changes in one shot
|
|
216394
216314
|
driftless context list List all context topics
|
|
216395
216315
|
driftless context get <topic> Live view of one topic before editing
|
|
216396
216316
|
driftless context get --diff What context matters for my local changes?
|
|
@@ -216405,9 +216325,9 @@ Setup:
|
|
|
216405
216325
|
Commands:
|
|
216406
216326
|
login Authenticate with API key
|
|
216407
216327
|
init Smart init: scan \u2192 detect patterns \u2192 create topics + rules + anchor docs
|
|
216328
|
+
sync Load context for current changes + scan in one shot
|
|
216408
216329
|
scan Evaluate staged + uncommitted changes against rules
|
|
216409
216330
|
scan --diff Evaluate uncommitted changes only
|
|
216410
|
-
session Agent session: start (context) \u2192 end (scan + report)
|
|
216411
216331
|
context Live repo context (topics, search, anchors)
|
|
216412
216332
|
install-skill Install AGENTS.md into current repo
|
|
216413
216333
|
doctor Check environment health (auth, API, git, workspace, repo, baseline)
|
|
@@ -216429,10 +216349,6 @@ Context subcommands:
|
|
|
216429
216349
|
delete <topic> Delete a topic
|
|
216430
216350
|
load --files "p1,p2" Match topics by file paths
|
|
216431
216351
|
|
|
216432
|
-
Session subcommands:
|
|
216433
|
-
start [--files "p1,p2"] Show relevant context before editing
|
|
216434
|
-
end Scan changes, check violations, suggest updates
|
|
216435
|
-
|
|
216436
216352
|
Flags:
|
|
216437
216353
|
--json Output as JSON (default is human-readable)
|
|
216438
216354
|
--dry-run Preview changes without writing to Driftless Cloud
|
|
@@ -216450,8 +216366,8 @@ Output format:
|
|
|
216450
216366
|
Use --json flag for machine-readable output (agents, CI).
|
|
216451
216367
|
|
|
216452
216368
|
Examples:
|
|
216453
|
-
driftless
|
|
216454
|
-
driftless
|
|
216369
|
+
driftless sync # context + scan for current changes
|
|
216370
|
+
driftless sync --json # machine-readable output
|
|
216455
216371
|
|
|
216456
216372
|
driftless context add "b2b-guard" \\
|
|
216457
216373
|
--what "Guard que protege endpoints B2B" \\
|
|
@@ -216597,8 +216513,8 @@ async function main() {
|
|
|
216597
216513
|
case "context":
|
|
216598
216514
|
await contextCommand(args.slice(1));
|
|
216599
216515
|
break;
|
|
216600
|
-
case "
|
|
216601
|
-
await
|
|
216516
|
+
case "sync":
|
|
216517
|
+
await syncCommand(args.slice(1));
|
|
216602
216518
|
break;
|
|
216603
216519
|
case "install-skill":
|
|
216604
216520
|
await installSkillCommand();
|