@driftless-sh/cli 0.1.34 → 0.1.35
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 +46 -24
- package/dist/index.js +181 -77
- package/dist/index.js.map +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,10 +12,11 @@ npm install -g @driftless-sh/cli
|
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
14
|
driftless login --key <api-key> # Generate one at driftless.icu → Settings → API keys
|
|
15
|
-
driftless init # Scan repo,
|
|
16
|
-
driftless
|
|
15
|
+
driftless init # Scan repo, upload baseline, install skill
|
|
16
|
+
driftless init --suggest # Also auto-generate rules + context topics
|
|
17
|
+
driftless sync # Pull stale topics, violations, and suggested topics
|
|
18
|
+
driftless context get <slug> # Load context before editing
|
|
17
19
|
driftless scan --diff # Check changes against rules before pushing
|
|
18
|
-
driftless session end # Scan + context report + stale detection
|
|
19
20
|
driftless install-skill # Write AGENTS.md for Claude Code / Cursor
|
|
20
21
|
```
|
|
21
22
|
|
|
@@ -48,34 +49,38 @@ Auth is stored in `~/.driftless/config.json`. You can also set `DRIFTLESS_API_KE
|
|
|
48
49
|
|
|
49
50
|
### `driftless init`
|
|
50
51
|
|
|
51
|
-
|
|
52
|
+
Smart repo initialization:
|
|
52
53
|
|
|
53
|
-
1. **Identity** — detects framework, system type, auth patterns (zero tokens)
|
|
54
|
-
2. **AST extraction** — extracts endpoints, guards, services, modules,
|
|
54
|
+
1. **Identity** — detects framework, system type, auth patterns (local, zero tokens)
|
|
55
|
+
2. **AST extraction** — extracts endpoints, guards, services, modules, relations (local, zero tokens)
|
|
55
56
|
3. **Upload** — sends baseline + components + relations to Cloud
|
|
57
|
+
4. **Skill install** — writes `AGENTS.md` and `.driftless/skill.md`
|
|
56
58
|
|
|
57
59
|
```bash
|
|
58
60
|
cd my-nestjs-repo
|
|
59
61
|
driftless init
|
|
60
62
|
```
|
|
61
63
|
|
|
62
|
-
|
|
64
|
+
#### Flags
|
|
63
65
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
The agent loop — start with context, finish with a report.
|
|
66
|
+
| Flag | Description |
|
|
67
|
+
|------|-------------|
|
|
68
|
+
| `--suggest` | Also auto-generate rules and context topics from detected patterns. Off by default. |
|
|
69
|
+
| `--src <path>` | **Monorepo support** — scan from a subpath while git root stays at cwd. Example: `--src apps/api/src` |
|
|
69
70
|
|
|
70
71
|
```bash
|
|
71
|
-
|
|
72
|
-
driftless
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
# Standard repo
|
|
73
|
+
driftless init
|
|
74
|
+
|
|
75
|
+
# Standard repo with auto-generated topics + rules
|
|
76
|
+
driftless init --suggest
|
|
75
77
|
|
|
76
|
-
|
|
78
|
+
# Monorepo: NestJS app lives at apps/api/src
|
|
79
|
+
driftless init --src apps/api/src
|
|
77
80
|
|
|
78
|
-
|
|
81
|
+
# Monorepo with auto-generated topics + rules
|
|
82
|
+
driftless init --src apps/api/src --suggest
|
|
83
|
+
```
|
|
79
84
|
|
|
80
85
|
---
|
|
81
86
|
|
|
@@ -153,7 +158,7 @@ driftless context sync auth --doc docs/auth.md --dry-run
|
|
|
153
158
|
|
|
154
159
|
A topic can track components in two ways:
|
|
155
160
|
|
|
156
|
-
- **`--pattern`** — glob pattern that resolves dynamically (e.g. `src/sdk/**`)
|
|
161
|
+
- **`--pattern`** — glob pattern that resolves dynamically (e.g. `src/sdk/**`). One glob per topic — commas are not supported.
|
|
157
162
|
- **`--where`** — explicit file path
|
|
158
163
|
|
|
159
164
|
```bash
|
|
@@ -173,6 +178,17 @@ driftless context add "b2b-guard" \
|
|
|
173
178
|
--how "Verifies org_id in the token"
|
|
174
179
|
```
|
|
175
180
|
|
|
181
|
+
#### Appending multiple gotchas at once
|
|
182
|
+
|
|
183
|
+
`--gotcha` can be passed multiple times in one call — all values are appended:
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
driftless context update auth \
|
|
187
|
+
--gotcha "Reset token on org switch" \
|
|
188
|
+
--gotcha "Test tokens never include production claims" \
|
|
189
|
+
--gotcha "Clerk JWTs expire after 1h"
|
|
190
|
+
```
|
|
191
|
+
|
|
176
192
|
#### Fields
|
|
177
193
|
|
|
178
194
|
| Field | Description |
|
|
@@ -254,6 +270,11 @@ Show help for all commands or a specific command.
|
|
|
254
270
|
driftless help # All commands
|
|
255
271
|
driftless help context # Context subcommands
|
|
256
272
|
driftless help scan # Scan details
|
|
273
|
+
|
|
274
|
+
# --help also works on any command without hitting the API
|
|
275
|
+
driftless init --help
|
|
276
|
+
driftless scan --help
|
|
277
|
+
driftless context --help
|
|
257
278
|
```
|
|
258
279
|
|
|
259
280
|
---
|
|
@@ -283,14 +304,15 @@ This creates `AGENTS.md` with instructions for Claude Code to use Driftless befo
|
|
|
283
304
|
### Custom Agents
|
|
284
305
|
|
|
285
306
|
```bash
|
|
286
|
-
#
|
|
287
|
-
driftless
|
|
307
|
+
# Load context for the files you're about to touch
|
|
308
|
+
driftless context get <slug>
|
|
309
|
+
driftless context load --files "src/auth/**"
|
|
288
310
|
|
|
289
|
-
# Before
|
|
290
|
-
driftless
|
|
311
|
+
# Before pushing
|
|
312
|
+
driftless scan --diff
|
|
291
313
|
|
|
292
314
|
# After discovering new context
|
|
293
|
-
driftless context
|
|
315
|
+
driftless context update <slug> --gotcha "..." --decisions "..."
|
|
294
316
|
```
|
|
295
317
|
|
|
296
318
|
Use `--json` flag for machine-readable output that agents can parse.
|
package/dist/index.js
CHANGED
|
@@ -214567,7 +214567,7 @@ async function installSkillCommand() {
|
|
|
214567
214567
|
// src/commands/init.ts
|
|
214568
214568
|
function getVersion() {
|
|
214569
214569
|
try {
|
|
214570
|
-
return "0.1.
|
|
214570
|
+
return "0.1.35";
|
|
214571
214571
|
} catch {
|
|
214572
214572
|
return "0.0.0";
|
|
214573
214573
|
}
|
|
@@ -214866,6 +214866,15 @@ function step(label, value) {
|
|
|
214866
214866
|
}
|
|
214867
214867
|
async function initCommand(args) {
|
|
214868
214868
|
const version = getVersion();
|
|
214869
|
+
let srcOverride;
|
|
214870
|
+
let suggest = false;
|
|
214871
|
+
for (let i = 0; i < args.length; i++) {
|
|
214872
|
+
if (args[i] === "--src" && i + 1 < args.length && !args[i + 1].startsWith("--")) {
|
|
214873
|
+
srcOverride = args[++i];
|
|
214874
|
+
} else if (args[i] === "--suggest") {
|
|
214875
|
+
suggest = true;
|
|
214876
|
+
}
|
|
214877
|
+
}
|
|
214869
214878
|
console.log("D R I F T L E S S");
|
|
214870
214879
|
console.log(`v${version}`);
|
|
214871
214880
|
console.log("");
|
|
@@ -214901,10 +214910,12 @@ async function initCommand(args) {
|
|
|
214901
214910
|
const workspaceSlug = workspace.slug;
|
|
214902
214911
|
console.log(`Repository: ${remote.org}/${remote.repo}`);
|
|
214903
214912
|
console.log(`Workspace: ${workspaceSlug} \u2713`);
|
|
214913
|
+
if (srcOverride) console.log(`Scan root: ${srcOverride}`);
|
|
214904
214914
|
console.log("");
|
|
214905
214915
|
const cwd = process.cwd();
|
|
214916
|
+
const scanRoot = srcOverride ? (0, import_node_path4.resolve)(cwd, srcOverride) : cwd;
|
|
214906
214917
|
const scanStart = Date.now();
|
|
214907
|
-
const scanResult = await (0, import_scanner.scanRepo)(
|
|
214918
|
+
const scanResult = await (0, import_scanner.scanRepo)(scanRoot);
|
|
214908
214919
|
const scanMs = Date.now() - scanStart;
|
|
214909
214920
|
const earlyPatterns = analyzeCodePatterns(scanResult.components);
|
|
214910
214921
|
const summary = {
|
|
@@ -214976,46 +214987,53 @@ async function initCommand(args) {
|
|
|
214976
214987
|
step("uploading components", "failed (continuing)");
|
|
214977
214988
|
}
|
|
214978
214989
|
}
|
|
214979
|
-
const patterns = earlyPatterns;
|
|
214980
|
-
const smartRules = generateSmartRules(repo.id, patterns);
|
|
214981
214990
|
let rulesCreated = 0;
|
|
214982
|
-
|
|
214983
|
-
|
|
214984
|
-
|
|
214985
|
-
|
|
214986
|
-
|
|
214991
|
+
if (suggest) {
|
|
214992
|
+
const smartRules = generateSmartRules(repo.id, earlyPatterns);
|
|
214993
|
+
let rulesSkipped = 0;
|
|
214994
|
+
for (const rule of smartRules) {
|
|
214995
|
+
try {
|
|
214996
|
+
const result = await api.post(`/workspaces/${workspaceSlug}/rules`, { ...rule, auto_suggested: true });
|
|
214997
|
+
if (result?.skipped) rulesSkipped++;
|
|
214998
|
+
else rulesCreated++;
|
|
214999
|
+
} catch {
|
|
214987
215000
|
rulesSkipped++;
|
|
214988
|
-
} else {
|
|
214989
|
-
rulesCreated++;
|
|
214990
215001
|
}
|
|
214991
|
-
} catch {
|
|
214992
|
-
rulesSkipped++;
|
|
214993
215002
|
}
|
|
215003
|
+
step("creating rules", `${rulesCreated} created \xB7 ${rulesSkipped} skipped \u2713`);
|
|
215004
|
+
} else {
|
|
215005
|
+
step("creating rules", "skipped (use --suggest to auto-generate)");
|
|
214994
215006
|
}
|
|
214995
|
-
step("creating rules", `${rulesCreated} created \xB7 ${rulesSkipped} skipped \u2713`);
|
|
214996
|
-
const smartWatchers = generateSmartWatchers(components);
|
|
214997
215007
|
let watchersCreated = 0;
|
|
214998
|
-
|
|
214999
|
-
|
|
215000
|
-
|
|
215001
|
-
|
|
215002
|
-
|
|
215003
|
-
|
|
215004
|
-
|
|
215005
|
-
|
|
215006
|
-
|
|
215007
|
-
|
|
215008
|
-
|
|
215009
|
-
|
|
215010
|
-
|
|
215011
|
-
|
|
215012
|
-
|
|
215013
|
-
|
|
215014
|
-
|
|
215015
|
-
|
|
215008
|
+
if (suggest) {
|
|
215009
|
+
const smartWatchers = generateSmartWatchers(components);
|
|
215010
|
+
let watchersSkipped = 0;
|
|
215011
|
+
for (const watcher of smartWatchers) {
|
|
215012
|
+
try {
|
|
215013
|
+
await api.post(`/workspaces/${workspaceSlug}/watchers`, {
|
|
215014
|
+
name: watcher.name,
|
|
215015
|
+
what: watcher.what,
|
|
215016
|
+
how: watcher.how,
|
|
215017
|
+
pattern: watcher.pattern,
|
|
215018
|
+
decisions: watcher.decisions,
|
|
215019
|
+
gotchas: watcher.gotchas,
|
|
215020
|
+
ownership: watcher.ownership,
|
|
215021
|
+
where_repos: [repo.id],
|
|
215022
|
+
created_by: "driftless-init",
|
|
215023
|
+
suggested: true,
|
|
215024
|
+
origin: "auto",
|
|
215025
|
+
status: "draft",
|
|
215026
|
+
kind: "domain-map"
|
|
215027
|
+
});
|
|
215028
|
+
watchersCreated++;
|
|
215029
|
+
} catch {
|
|
215030
|
+
watchersSkipped++;
|
|
215031
|
+
}
|
|
215016
215032
|
}
|
|
215033
|
+
step("creating context", `${watchersCreated} topics \u2713`);
|
|
215034
|
+
} else {
|
|
215035
|
+
step("creating context", "skipped (use --suggest to auto-generate)");
|
|
215017
215036
|
}
|
|
215018
|
-
step("creating context", `${watchersCreated} watchers \u2713`);
|
|
215019
215037
|
const existingDocs = detectExistingDocs(cwd);
|
|
215020
215038
|
step("detecting docs", `${existingDocs.length} found (not auto-synced)`);
|
|
215021
215039
|
let skillLine = "skipped";
|
|
@@ -215029,8 +215047,8 @@ async function initCommand(args) {
|
|
|
215029
215047
|
step("installing skill", skillLine);
|
|
215030
215048
|
console.log("");
|
|
215031
215049
|
console.log("\u2713 repo context bootstrapped");
|
|
215032
|
-
console.log(` \u251C\u2500
|
|
215033
|
-
console.log(` \u251C\u2500 rules ${rulesCreated}`);
|
|
215050
|
+
console.log(` \u251C\u2500 topics ${suggest ? watchersCreated : "(use --suggest to generate)"}`);
|
|
215051
|
+
console.log(` \u251C\u2500 rules ${suggest ? rulesCreated : "(use --suggest to generate)"}`);
|
|
215034
215052
|
console.log(` \u251C\u2500 docs found ${existingDocs.length} (ask your agent to sync them)`);
|
|
215035
215053
|
console.log(` \u251C\u2500 components ${components.length}`);
|
|
215036
215054
|
console.log(` \u2514\u2500 relations ${relationCount}`);
|
|
@@ -215212,10 +215230,12 @@ function renderSummaryHuman(items) {
|
|
|
215212
215230
|
}
|
|
215213
215231
|
const topicWidth = Math.max(...items.map((i) => i.topic.length), 12) + 2;
|
|
215214
215232
|
const badgeWidth = Math.max(...items.map((i) => formatBadges(i.badges).length), 8) + 2;
|
|
215233
|
+
const statusWidth = Math.max(...items.map((i) => (i.classification?.status || "").length), 8) + 2;
|
|
215215
215234
|
for (const item of items) {
|
|
215216
215235
|
const topic = pad(item.topic, topicWidth);
|
|
215217
215236
|
const badges = pad(formatBadges(item.badges), badgeWidth);
|
|
215218
|
-
|
|
215237
|
+
const status = pad(item.classification?.status || "", statusWidth);
|
|
215238
|
+
console.log(`${topic}${status}${badges}${item.summary}`);
|
|
215219
215239
|
}
|
|
215220
215240
|
console.log(`
|
|
215221
215241
|
${items.length} topic${items.length === 1 ? "" : "s"}.`);
|
|
@@ -215224,11 +215244,19 @@ function renderContextHuman(ctx) {
|
|
|
215224
215244
|
console.log(`\u258C ${ctx.topic}`);
|
|
215225
215245
|
console.log(` ${ctx.summary}`);
|
|
215226
215246
|
if (ctx.version !== void 0) console.log(` version: ${ctx.version}`);
|
|
215247
|
+
if (ctx.classification) {
|
|
215248
|
+
const { origin, status, kind } = ctx.classification;
|
|
215249
|
+
console.log(` origin: ${origin} status: ${status} kind: ${kind}`);
|
|
215250
|
+
}
|
|
215227
215251
|
if (ctx.stale.is_stale) {
|
|
215228
215252
|
console.log(`
|
|
215229
215253
|
\u26A0 STALE: ${ctx.stale.reason || "Context may be outdated"}`);
|
|
215230
215254
|
console.log(` \u2192 driftless context update ${ctx.topic} --what "..." --how "..."`);
|
|
215231
215255
|
}
|
|
215256
|
+
if (ctx.metadata.created_by === "driftless-init") {
|
|
215257
|
+
console.log(`
|
|
215258
|
+
\u24D8 auto-suggested by init \u2014 confirm with \`driftless context update ${ctx.topic} --what "..."\``);
|
|
215259
|
+
}
|
|
215232
215260
|
console.log("");
|
|
215233
215261
|
if (ctx.description.what) console.log(` what: ${ctx.description.what}`);
|
|
215234
215262
|
if (ctx.description.how) console.log(` how: ${ctx.description.how}`);
|
|
@@ -215357,9 +215385,23 @@ async function contextCommand(args) {
|
|
|
215357
215385
|
if (flags["docs"]) query.push("docs=true");
|
|
215358
215386
|
if (flags["auto"]) query.push("auto=true");
|
|
215359
215387
|
if (flags["manual"]) query.push("manual=true");
|
|
215360
|
-
if (flags["repo"]) query.push(`repo=${encodeURIComponent(flags["repo"])}`);
|
|
215361
215388
|
if (flags["suggested"]) query.push("suggested=true");
|
|
215362
215389
|
else query.push("suggested=false");
|
|
215390
|
+
if (flags["status"]) query.push(`status=${encodeURIComponent(flags["status"])}`);
|
|
215391
|
+
if (flags["kind"]) query.push(`kind=${encodeURIComponent(flags["kind"])}`);
|
|
215392
|
+
if (flags["repo"]) {
|
|
215393
|
+
query.push(`repo=${encodeURIComponent(flags["repo"])}`);
|
|
215394
|
+
} else if (flags["auto"]) {
|
|
215395
|
+
const currentRemote = getGitRemote();
|
|
215396
|
+
if (currentRemote) {
|
|
215397
|
+
try {
|
|
215398
|
+
const repos = await api.get(`/workspaces/${workspaceSlug}/repos`);
|
|
215399
|
+
const match = repos.find((r) => r.github_org === currentRemote.org && r.github_repo === currentRemote.repo);
|
|
215400
|
+
if (match) query.push(`repo=${encodeURIComponent(match.id)}`);
|
|
215401
|
+
} catch {
|
|
215402
|
+
}
|
|
215403
|
+
}
|
|
215404
|
+
}
|
|
215363
215405
|
const qs = query.length > 0 ? `?${query.join("&")}` : "";
|
|
215364
215406
|
try {
|
|
215365
215407
|
const summaries = await api.get(`/workspaces/${workspaceSlug}/watchers${qs}`);
|
|
@@ -215495,6 +215537,9 @@ async function contextCommand(args) {
|
|
|
215495
215537
|
}
|
|
215496
215538
|
const pattern = flags["pattern"];
|
|
215497
215539
|
if (pattern && !isJSON) {
|
|
215540
|
+
if (pattern.includes(",")) {
|
|
215541
|
+
console.error(`Warning: pattern "${pattern}" contains a comma. Only one glob is accepted per topic. Create separate topics or remove the comma.`);
|
|
215542
|
+
}
|
|
215498
215543
|
const localCount = countLocalFilesMatching(pattern);
|
|
215499
215544
|
if (localCount === 0) {
|
|
215500
215545
|
console.error(`Warning: pattern "${pattern}" matches 0 files locally. Topic created anyway.`);
|
|
@@ -215510,7 +215555,9 @@ async function contextCommand(args) {
|
|
|
215510
215555
|
decisions: flags["decisions"],
|
|
215511
215556
|
gotchas: flags["gotchas"],
|
|
215512
215557
|
ownership: flags["ownership"],
|
|
215513
|
-
file_content: fileContent
|
|
215558
|
+
file_content: fileContent,
|
|
215559
|
+
status: flags["status"],
|
|
215560
|
+
kind: flags["kind"]
|
|
215514
215561
|
});
|
|
215515
215562
|
analyticsEvent("context_created", workspaceSlug, { topic: name, has_pattern: !!pattern, has_what: !!flags["what"] });
|
|
215516
215563
|
if (isJSON) {
|
|
@@ -215541,12 +215588,21 @@ async function contextCommand(args) {
|
|
|
215541
215588
|
if (flags["decisions"]) updates.decisions = flags["decisions"];
|
|
215542
215589
|
if (flags["gotchas"]) updates.gotchas = flags["gotchas"];
|
|
215543
215590
|
if (flags["ownership"]) updates.ownership = flags["ownership"];
|
|
215544
|
-
|
|
215545
|
-
|
|
215546
|
-
const
|
|
215547
|
-
|
|
215548
|
-
|
|
215549
|
-
|
|
215591
|
+
if (flags["status"]) updates.status = flags["status"];
|
|
215592
|
+
const gotchaValues = [];
|
|
215593
|
+
const invariantValues = [];
|
|
215594
|
+
const checkValues = [];
|
|
215595
|
+
for (let i = 0; i < args.length - 1; i++) {
|
|
215596
|
+
const a = args[i];
|
|
215597
|
+
const b = args[i + 1];
|
|
215598
|
+
if (!b || b.startsWith("--")) continue;
|
|
215599
|
+
if (a === "--gotcha" || a === "--gotchas_append") gotchaValues.push(b);
|
|
215600
|
+
if (a === "--invariant") invariantValues.push(b);
|
|
215601
|
+
if (a === "--check") checkValues.push(b);
|
|
215602
|
+
}
|
|
215603
|
+
if (gotchaValues.length > 0) updates._append_gotchas = gotchaValues.join("\n");
|
|
215604
|
+
if (invariantValues.length > 0) updates._append_invariants = invariantValues.join("\n");
|
|
215605
|
+
if (checkValues.length > 0) updates._append_required_checks = checkValues.join("\n");
|
|
215550
215606
|
const enforceFlag = flags["enforce"];
|
|
215551
215607
|
if (enforceFlag && typeof enforceFlag === "string") {
|
|
215552
215608
|
updates._enforce_rule = enforceFlag;
|
|
@@ -215554,16 +215610,18 @@ async function contextCommand(args) {
|
|
|
215554
215610
|
updates._enforce_rule = true;
|
|
215555
215611
|
}
|
|
215556
215612
|
const eventParts = [];
|
|
215557
|
-
if (
|
|
215558
|
-
if (
|
|
215559
|
-
if (
|
|
215613
|
+
if (gotchaValues.length > 0) eventParts.push(`gotcha appended`);
|
|
215614
|
+
if (invariantValues.length > 0) eventParts.push(`invariant appended`);
|
|
215615
|
+
if (checkValues.length > 0) eventParts.push(`check appended`);
|
|
215560
215616
|
if (enforceFlag) eventParts.push(`enforce requested`);
|
|
215561
215617
|
if (eventParts.length > 0) {
|
|
215562
215618
|
updates._event_detail = `CONTEXT_UPDATED: ${eventParts.join(", ")}`;
|
|
215563
215619
|
}
|
|
215564
215620
|
if (Object.keys(updates).length === 0) {
|
|
215565
|
-
console.error("No fields to update.
|
|
215566
|
-
console.error(
|
|
215621
|
+
console.error("No fields to update.");
|
|
215622
|
+
console.error("Scalar: --what, --how, --pattern, --decisions, --gotchas, --ownership");
|
|
215623
|
+
console.error("Append: --gotcha (repeatable), --invariant (repeatable), --check (repeatable)");
|
|
215624
|
+
console.error('Enforce: --enforce "<rule-description>"');
|
|
215567
215625
|
process.exit(1);
|
|
215568
215626
|
}
|
|
215569
215627
|
const currentRemote = getGitRemote();
|
|
@@ -215672,7 +215730,12 @@ async function contextCommand(args) {
|
|
|
215672
215730
|
}
|
|
215673
215731
|
const updates = { last_updated: /* @__PURE__ */ new Date() };
|
|
215674
215732
|
if (fileContent) updates.file_content = fileContent;
|
|
215675
|
-
if (docFlag)
|
|
215733
|
+
if (docFlag) {
|
|
215734
|
+
updates.anchored_doc_path = docFlag;
|
|
215735
|
+
updates.origin = "doc";
|
|
215736
|
+
updates.status = "draft";
|
|
215737
|
+
updates.kind = "docs-pending";
|
|
215738
|
+
}
|
|
215676
215739
|
if (noteFlag) updates.decisions = noteFlag;
|
|
215677
215740
|
if (filesFlag) updates.where_files = filesFlag.split(",").map((f) => f.trim()).filter(Boolean);
|
|
215678
215741
|
if (patternFlag) updates.pattern = patternFlag;
|
|
@@ -216004,18 +216067,18 @@ async function syncCommand(args) {
|
|
|
216004
216067
|
}
|
|
216005
216068
|
if (!repoId) {
|
|
216006
216069
|
if (!isJSON) {
|
|
216007
|
-
console.log(
|
|
216008
|
-
console.log("Run driftless init to register this repo
|
|
216070
|
+
console.log(`\u26A0 Repo '${remote.org}/${remote.repo}' not found in workspace '${workspaceSlug}'.`);
|
|
216071
|
+
console.log(" Run `driftless init` to register this repo, or `driftless doctor` to diagnose.");
|
|
216009
216072
|
} else {
|
|
216010
|
-
emitJSON3({ error: "repo_not_found", repo: `${remote.org}/${remote.repo}
|
|
216073
|
+
emitJSON3({ error: "repo_not_found", repo: `${remote.org}/${remote.repo}`, workspace: workspaceSlug });
|
|
216011
216074
|
}
|
|
216012
|
-
process.exit(
|
|
216075
|
+
process.exit(1);
|
|
216013
216076
|
}
|
|
216014
216077
|
const [eventsRes, staleTopics, violations, suggestedTopics] = await Promise.allSettled([
|
|
216015
216078
|
api.get(`/workspaces/${workspaceSlug}/watchers/events?repo_id=${repoId}&limit=20`),
|
|
216016
216079
|
api.get(`/workspaces/${workspaceSlug}/watchers?stale=true&repo=${repoId}`),
|
|
216017
216080
|
api.get(`/workspaces/${workspaceSlug}/violations?repo_id=${repoId}&status=open&limit=20`),
|
|
216018
|
-
api.get(`/workspaces/${workspaceSlug}/watchers?suggested=true`)
|
|
216081
|
+
api.get(`/workspaces/${workspaceSlug}/watchers?suggested=true&repo=${repoId}`)
|
|
216019
216082
|
]);
|
|
216020
216083
|
const events = eventsRes.status === "fulfilled" ? eventsRes.value.events ?? [] : [];
|
|
216021
216084
|
const stale = staleTopics.status === "fulfilled" ? staleTopics.value : [];
|
|
@@ -216264,13 +216327,18 @@ async function doctorCommand() {
|
|
|
216264
216327
|
if (ghApp) {
|
|
216265
216328
|
checks.push({ name: "GitHub App", status: "ok", detail: "Installed and active" });
|
|
216266
216329
|
} else {
|
|
216267
|
-
checks.push({ name: "GitHub App", status: "warn", detail: "Not installed
|
|
216330
|
+
checks.push({ name: "GitHub App", status: "warn", detail: "Not installed \u2014 driftless.icu/ecosystem \u2192 Settings \u2192 Integrations (if just installed, wait ~60s)" });
|
|
216331
|
+
}
|
|
216332
|
+
} catch (err) {
|
|
216333
|
+
const isNotFound = err?.status === 404 || String(err?.message ?? "").includes("404");
|
|
216334
|
+
if (isNotFound) {
|
|
216335
|
+
checks.push({ name: "GitHub App", status: "warn", detail: "Not installed \u2014 driftless.icu/ecosystem \u2192 Settings \u2192 Integrations" });
|
|
216336
|
+
} else {
|
|
216337
|
+
checks.push({ name: "GitHub App", status: "warn", detail: "Could not verify \u2014 check your connection or API key" });
|
|
216268
216338
|
}
|
|
216269
|
-
} catch {
|
|
216270
|
-
checks.push({ name: "GitHub App", status: "warn", detail: "Could not verify" });
|
|
216271
216339
|
}
|
|
216272
216340
|
} else {
|
|
216273
|
-
checks.push({ name: "GitHub App", status: "warn", detail: "Could not verify" });
|
|
216341
|
+
checks.push({ name: "GitHub App", status: "warn", detail: "Could not verify (workspace unknown)" });
|
|
216274
216342
|
}
|
|
216275
216343
|
} else {
|
|
216276
216344
|
checks.push({ name: "GitHub App", status: "warn", detail: "Skipped (not a git repo)" });
|
|
@@ -216302,7 +216370,7 @@ function pad2(s, n) {
|
|
|
216302
216370
|
}
|
|
216303
216371
|
|
|
216304
216372
|
// src/index.ts
|
|
216305
|
-
var VERSION = "0.1.
|
|
216373
|
+
var VERSION = "0.1.35";
|
|
216306
216374
|
var HELP_TEXT = `Driftless CLI v${VERSION} \u2014 Living repo context for humans and coding agents
|
|
216307
216375
|
|
|
216308
216376
|
Install: npm install -g @driftless-sh/cli
|
|
@@ -216319,7 +216387,7 @@ Agent loop:
|
|
|
216319
216387
|
|
|
216320
216388
|
Setup:
|
|
216321
216389
|
driftless login --key <api-key> Authenticate
|
|
216322
|
-
driftless init
|
|
216390
|
+
driftless init [--src <path>] Scan repo, bootstrap context (add --suggest for auto topics+rules)
|
|
216323
216391
|
driftless doctor Check environment health
|
|
216324
216392
|
|
|
216325
216393
|
Commands:
|
|
@@ -216370,8 +216438,8 @@ Examples:
|
|
|
216370
216438
|
driftless sync --json # machine-readable output
|
|
216371
216439
|
|
|
216372
216440
|
driftless context add "b2b-guard" \\
|
|
216373
|
-
--what "Guard
|
|
216374
|
-
--how "
|
|
216441
|
+
--what "Guard protecting B2B endpoints" \\
|
|
216442
|
+
--how "Verifies org_id from the auth token" \\
|
|
216375
216443
|
--pattern "src/shared/guards/**"
|
|
216376
216444
|
|
|
216377
216445
|
driftless context get sdk
|
|
@@ -216393,22 +216461,30 @@ Options:
|
|
|
216393
216461
|
Example:
|
|
216394
216462
|
driftless login --key drift_xxx
|
|
216395
216463
|
`,
|
|
216396
|
-
init: `driftless init
|
|
216464
|
+
init: `driftless init [--src <path>] [--suggest]
|
|
216397
216465
|
|
|
216398
216466
|
Smart initialization \u2014 zero friction:
|
|
216399
216467
|
1. Detects framework, system type, auth patterns (Pass 1)
|
|
216400
216468
|
2. AST scan: extracts endpoints, guards, services, modules, relations (Pass 2)
|
|
216401
216469
|
3. Analyzes code patterns: guard enforcement, multi-tenant, large files, legacy
|
|
216402
216470
|
4. Uploads baseline + components + relations to Cloud
|
|
216403
|
-
5.
|
|
216404
|
-
6.
|
|
216405
|
-
|
|
216471
|
+
5. Detects existing docs (not auto-synced \u2014 ask your agent to sync them)
|
|
216472
|
+
6. Installs AGENTS.md skill
|
|
216473
|
+
|
|
216474
|
+
With --suggest (optional):
|
|
216475
|
+
Also auto-generates rules and context topics from detected patterns.
|
|
216476
|
+
|
|
216477
|
+
Options:
|
|
216478
|
+
--src <path> Scan root relative to cwd (monorepo support).
|
|
216479
|
+
Git root stays at cwd; scanner starts from <path>.
|
|
216480
|
+
--suggest Auto-generate rules and context topics. Off by default.
|
|
216406
216481
|
|
|
216407
216482
|
Must be run from a git repository root.
|
|
216408
216483
|
|
|
216409
|
-
|
|
216410
|
-
cd my-nestjs-repo
|
|
216484
|
+
Examples:
|
|
216411
216485
|
driftless init
|
|
216486
|
+
driftless init --src apps/api/src
|
|
216487
|
+
driftless init --src apps/api/src --suggest
|
|
216412
216488
|
`,
|
|
216413
216489
|
scan: `driftless scan [--diff]
|
|
216414
216490
|
|
|
@@ -216505,25 +216581,53 @@ async function main() {
|
|
|
216505
216581
|
const command = args[0];
|
|
216506
216582
|
switch (command) {
|
|
216507
216583
|
case "init":
|
|
216508
|
-
|
|
216584
|
+
if (args[1] === "--help") {
|
|
216585
|
+
showCommandHelp("init");
|
|
216586
|
+
} else {
|
|
216587
|
+
await initCommand(args.slice(1));
|
|
216588
|
+
}
|
|
216509
216589
|
break;
|
|
216510
216590
|
case "scan":
|
|
216511
|
-
|
|
216591
|
+
if (args[1] === "--help") {
|
|
216592
|
+
showCommandHelp("scan");
|
|
216593
|
+
} else {
|
|
216594
|
+
await scanCommand(args.slice(1));
|
|
216595
|
+
}
|
|
216512
216596
|
break;
|
|
216513
216597
|
case "context":
|
|
216514
|
-
|
|
216598
|
+
if (args[1] === "--help") {
|
|
216599
|
+
showCommandHelp("context");
|
|
216600
|
+
} else {
|
|
216601
|
+
await contextCommand(args.slice(1));
|
|
216602
|
+
}
|
|
216515
216603
|
break;
|
|
216516
216604
|
case "sync":
|
|
216517
|
-
|
|
216605
|
+
if (args[1] === "--help") {
|
|
216606
|
+
showCommandHelp("sync");
|
|
216607
|
+
} else {
|
|
216608
|
+
await syncCommand(args.slice(1));
|
|
216609
|
+
}
|
|
216518
216610
|
break;
|
|
216519
216611
|
case "install-skill":
|
|
216520
|
-
|
|
216612
|
+
if (args[1] === "--help") {
|
|
216613
|
+
showCommandHelp("install-skill");
|
|
216614
|
+
} else {
|
|
216615
|
+
await installSkillCommand();
|
|
216616
|
+
}
|
|
216521
216617
|
break;
|
|
216522
216618
|
case "login":
|
|
216523
|
-
|
|
216619
|
+
if (args[1] === "--help") {
|
|
216620
|
+
showCommandHelp("login");
|
|
216621
|
+
} else {
|
|
216622
|
+
loginCommand(args.slice(1));
|
|
216623
|
+
}
|
|
216524
216624
|
break;
|
|
216525
216625
|
case "doctor":
|
|
216526
|
-
|
|
216626
|
+
if (args[1] === "--help") {
|
|
216627
|
+
showCommandHelp("doctor");
|
|
216628
|
+
} else {
|
|
216629
|
+
await doctorCommand();
|
|
216630
|
+
}
|
|
216527
216631
|
break;
|
|
216528
216632
|
case "help":
|
|
216529
216633
|
if (args[1]) {
|