@driftless-sh/cli 0.1.24 → 0.1.26
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 +11 -1
- package/dist/index.js +414 -272
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -23019,8 +23019,8 @@ ${lanes.join("\n")}
|
|
|
23019
23019
|
function containsInvalidEscapeFlag(node) {
|
|
23020
23020
|
return !!((node.templateFlags || 0) & 2048);
|
|
23021
23021
|
}
|
|
23022
|
-
function hasInvalidEscape(
|
|
23023
|
-
return
|
|
23022
|
+
function hasInvalidEscape(template) {
|
|
23023
|
+
return template && !!(isNoSubstitutionTemplateLiteral(template) ? containsInvalidEscapeFlag(template) : containsInvalidEscapeFlag(template.head) || some(template.templateSpans, (span) => containsInvalidEscapeFlag(span.literal)));
|
|
23024
23024
|
}
|
|
23025
23025
|
var doubleQuoteEscapedCharsRegExp = /[\\"\u0000-\u001f\u2028\u2029\u0085]/g;
|
|
23026
23026
|
var singleQuoteEscapedCharsRegExp = /[\\'\u0000-\u001f\u2028\u2029\u0085]/g;
|
|
@@ -30345,7 +30345,7 @@ ${lanes.join("\n")}
|
|
|
30345
30345
|
function updateNewExpression(node, expression, typeArguments, argumentsArray) {
|
|
30346
30346
|
return node.expression !== expression || node.typeArguments !== typeArguments || node.arguments !== argumentsArray ? update(createNewExpression(expression, typeArguments, argumentsArray), node) : node;
|
|
30347
30347
|
}
|
|
30348
|
-
function createTaggedTemplateExpression(tag, typeArguments,
|
|
30348
|
+
function createTaggedTemplateExpression(tag, typeArguments, template) {
|
|
30349
30349
|
const node = createBaseNode(
|
|
30350
30350
|
216
|
|
30351
30351
|
/* TaggedTemplateExpression */
|
|
@@ -30356,7 +30356,7 @@ ${lanes.join("\n")}
|
|
|
30356
30356
|
false
|
|
30357
30357
|
);
|
|
30358
30358
|
node.typeArguments = asNodeArray(typeArguments);
|
|
30359
|
-
node.template =
|
|
30359
|
+
node.template = template;
|
|
30360
30360
|
node.transformFlags |= propagateChildFlags(node.tag) | propagateChildrenFlags(node.typeArguments) | propagateChildFlags(node.template) | 1024;
|
|
30361
30361
|
if (node.typeArguments) {
|
|
30362
30362
|
node.transformFlags |= 1;
|
|
@@ -30366,8 +30366,8 @@ ${lanes.join("\n")}
|
|
|
30366
30366
|
}
|
|
30367
30367
|
return node;
|
|
30368
30368
|
}
|
|
30369
|
-
function updateTaggedTemplateExpression(node, tag, typeArguments,
|
|
30370
|
-
return node.tag !== tag || node.typeArguments !== typeArguments || node.template !==
|
|
30369
|
+
function updateTaggedTemplateExpression(node, tag, typeArguments, template) {
|
|
30370
|
+
return node.tag !== tag || node.typeArguments !== typeArguments || node.template !== template ? update(createTaggedTemplateExpression(tag, typeArguments, template), node) : node;
|
|
30371
30371
|
}
|
|
30372
30372
|
function createTypeAssertion(type, expression) {
|
|
30373
30373
|
const node = createBaseNode(
|
|
@@ -73219,14 +73219,14 @@ ${lanes.join("\n")}
|
|
|
73219
73219
|
while (i > 0) {
|
|
73220
73220
|
i--;
|
|
73221
73221
|
const t = types[i];
|
|
73222
|
-
if (t.flags & 128 && some(templates, (
|
|
73222
|
+
if (t.flags & 128 && some(templates, (template) => isTypeMatchedByTemplateLiteralOrStringMapping(t, template))) {
|
|
73223
73223
|
orderedRemoveItemAt(types, i);
|
|
73224
73224
|
}
|
|
73225
73225
|
}
|
|
73226
73226
|
}
|
|
73227
73227
|
}
|
|
73228
|
-
function isTypeMatchedByTemplateLiteralOrStringMapping(type,
|
|
73229
|
-
return
|
|
73228
|
+
function isTypeMatchedByTemplateLiteralOrStringMapping(type, template) {
|
|
73229
|
+
return template.flags & 134217728 ? isTypeMatchedByTemplateLiteralType(type, template) : isMemberOfStringMapping(type, template);
|
|
73230
73230
|
}
|
|
73231
73231
|
function removeConstrainedTypeVariables(types) {
|
|
73232
73232
|
const typeVariables = [];
|
|
@@ -85395,9 +85395,9 @@ ${lanes.join("\n")}
|
|
|
85395
85395
|
const signature = getDecoratorCallSignature(decorator);
|
|
85396
85396
|
return signature ? getOrCreateTypeFromSignature(signature) : void 0;
|
|
85397
85397
|
}
|
|
85398
|
-
function getContextualTypeForSubstitutionExpression(
|
|
85399
|
-
if (
|
|
85400
|
-
return getContextualTypeForArgument(
|
|
85398
|
+
function getContextualTypeForSubstitutionExpression(template, substitutionExpression) {
|
|
85399
|
+
if (template.parent.kind === 216) {
|
|
85400
|
+
return getContextualTypeForArgument(template.parent, substitutionExpression);
|
|
85401
85401
|
}
|
|
85402
85402
|
return void 0;
|
|
85403
85403
|
}
|
|
@@ -89022,10 +89022,10 @@ ${lanes.join("\n")}
|
|
|
89022
89022
|
return [createSyntheticExpression(node, emptyFreshJsxObjectType)];
|
|
89023
89023
|
}
|
|
89024
89024
|
if (node.kind === 216) {
|
|
89025
|
-
const
|
|
89026
|
-
const args2 = [createSyntheticExpression(
|
|
89027
|
-
if (
|
|
89028
|
-
forEach(
|
|
89025
|
+
const template = node.template;
|
|
89026
|
+
const args2 = [createSyntheticExpression(template, getGlobalTemplateStringsArrayType())];
|
|
89027
|
+
if (template.kind === 229) {
|
|
89028
|
+
forEach(template.templateSpans, (span) => {
|
|
89029
89029
|
args2.push(span.expression);
|
|
89030
89030
|
});
|
|
89031
89031
|
}
|
|
@@ -107909,18 +107909,18 @@ ${lanes.join("\n")}
|
|
|
107909
107909
|
const templateArguments = [void 0];
|
|
107910
107910
|
const cookedStrings = [];
|
|
107911
107911
|
const rawStrings = [];
|
|
107912
|
-
const
|
|
107913
|
-
if (level === 0 && !hasInvalidEscape(
|
|
107912
|
+
const template = node.template;
|
|
107913
|
+
if (level === 0 && !hasInvalidEscape(template)) {
|
|
107914
107914
|
return visitEachChild(node, visitor, context);
|
|
107915
107915
|
}
|
|
107916
107916
|
const { factory: factory2 } = context;
|
|
107917
|
-
if (isNoSubstitutionTemplateLiteral(
|
|
107918
|
-
cookedStrings.push(createTemplateCooked(factory2,
|
|
107919
|
-
rawStrings.push(getRawLiteral(factory2,
|
|
107917
|
+
if (isNoSubstitutionTemplateLiteral(template)) {
|
|
107918
|
+
cookedStrings.push(createTemplateCooked(factory2, template));
|
|
107919
|
+
rawStrings.push(getRawLiteral(factory2, template, currentSourceFile));
|
|
107920
107920
|
} else {
|
|
107921
|
-
cookedStrings.push(createTemplateCooked(factory2,
|
|
107922
|
-
rawStrings.push(getRawLiteral(factory2,
|
|
107923
|
-
for (const templateSpan of
|
|
107921
|
+
cookedStrings.push(createTemplateCooked(factory2, template.head));
|
|
107922
|
+
rawStrings.push(getRawLiteral(factory2, template.head, currentSourceFile));
|
|
107923
|
+
for (const templateSpan of template.templateSpans) {
|
|
107924
107924
|
cookedStrings.push(createTemplateCooked(factory2, templateSpan.literal));
|
|
107925
107925
|
rawStrings.push(getRawLiteral(factory2, templateSpan.literal, currentSourceFile));
|
|
107926
107926
|
templateArguments.push(Debug.checkDefined(visitNode(templateSpan.expression, visitor, isExpression)));
|
|
@@ -107950,8 +107950,8 @@ ${lanes.join("\n")}
|
|
|
107950
107950
|
templateArguments
|
|
107951
107951
|
);
|
|
107952
107952
|
}
|
|
107953
|
-
function createTemplateCooked(factory2,
|
|
107954
|
-
return
|
|
107953
|
+
function createTemplateCooked(factory2, template) {
|
|
107954
|
+
return template.templateFlags & 26656 ? factory2.createVoidZero() : factory2.createStringLiteral(template.text);
|
|
107955
107955
|
}
|
|
107956
107956
|
function getRawLiteral(factory2, node, currentSourceFile) {
|
|
107957
107957
|
let text = node.rawText;
|
|
@@ -114494,13 +114494,13 @@ ${lanes.join("\n")}
|
|
|
114494
114494
|
const boundTag = factory2.createFunctionBindCall(tag, classThis, []);
|
|
114495
114495
|
setOriginalNode(boundTag, node);
|
|
114496
114496
|
setTextRange(boundTag, node);
|
|
114497
|
-
const
|
|
114497
|
+
const template = visitNode(node.template, visitor, isTemplateLiteral);
|
|
114498
114498
|
return factory2.updateTaggedTemplateExpression(
|
|
114499
114499
|
node,
|
|
114500
114500
|
boundTag,
|
|
114501
114501
|
/*typeArguments*/
|
|
114502
114502
|
void 0,
|
|
114503
|
-
|
|
114503
|
+
template
|
|
114504
114504
|
);
|
|
114505
114505
|
}
|
|
114506
114506
|
return visitEachChild(node, visitor, context);
|
|
@@ -146943,7 +146943,7 @@ ${lanes.join("\n")}
|
|
|
146943
146943
|
};
|
|
146944
146944
|
}
|
|
146945
146945
|
function createBuildOrUpdateInvalidedProject(state, project, projectPath, projectIndex, config, status, buildOrder) {
|
|
146946
|
-
let
|
|
146946
|
+
let step2 = 0;
|
|
146947
146947
|
let program;
|
|
146948
146948
|
let buildResult;
|
|
146949
146949
|
return {
|
|
@@ -147019,14 +147019,14 @@ ${lanes.join("\n")}
|
|
|
147019
147019
|
if (state.options.dry) {
|
|
147020
147020
|
reportStatus(state, Diagnostics.A_non_dry_build_would_build_project_0, project);
|
|
147021
147021
|
buildResult = 1;
|
|
147022
|
-
|
|
147022
|
+
step2 = 2;
|
|
147023
147023
|
return;
|
|
147024
147024
|
}
|
|
147025
147025
|
if (state.options.verbose) reportStatus(state, Diagnostics.Building_project_0, project);
|
|
147026
147026
|
if (config.fileNames.length === 0) {
|
|
147027
147027
|
reportAndStoreErrors(state, projectPath, getConfigFileParsingDiagnostics(config));
|
|
147028
147028
|
buildResult = 0;
|
|
147029
|
-
|
|
147029
|
+
step2 = 2;
|
|
147030
147030
|
return;
|
|
147031
147031
|
}
|
|
147032
147032
|
const { host, compilerHost } = state;
|
|
@@ -147052,13 +147052,13 @@ ${lanes.join("\n")}
|
|
|
147052
147052
|
);
|
|
147053
147053
|
state.builderPrograms.set(projectPath, program);
|
|
147054
147054
|
}
|
|
147055
|
-
|
|
147055
|
+
step2++;
|
|
147056
147056
|
}
|
|
147057
147057
|
function emit(writeFileCallback, cancellationToken, customTransformers) {
|
|
147058
147058
|
var _a, _b, _c;
|
|
147059
147059
|
Debug.assertIsDefined(program);
|
|
147060
147060
|
Debug.assert(
|
|
147061
|
-
|
|
147061
|
+
step2 === 1
|
|
147062
147062
|
/* Emit */
|
|
147063
147063
|
);
|
|
147064
147064
|
const { host, compilerHost } = state;
|
|
@@ -147130,13 +147130,13 @@ ${lanes.join("\n")}
|
|
|
147130
147130
|
buildResult |= 4;
|
|
147131
147131
|
}
|
|
147132
147132
|
afterProgramDone(state, program);
|
|
147133
|
-
|
|
147133
|
+
step2 = 2;
|
|
147134
147134
|
return emitResult;
|
|
147135
147135
|
}
|
|
147136
147136
|
function executeSteps(till, cancellationToken, writeFile2, customTransformers) {
|
|
147137
|
-
while (
|
|
147138
|
-
const currentStep =
|
|
147139
|
-
switch (
|
|
147137
|
+
while (step2 <= till && step2 < 3) {
|
|
147138
|
+
const currentStep = step2;
|
|
147139
|
+
switch (step2) {
|
|
147140
147140
|
case 0:
|
|
147141
147141
|
createProgram2();
|
|
147142
147142
|
break;
|
|
@@ -147145,14 +147145,14 @@ ${lanes.join("\n")}
|
|
|
147145
147145
|
break;
|
|
147146
147146
|
case 2:
|
|
147147
147147
|
queueReferencingProjects(state, project, projectPath, projectIndex, config, buildOrder, Debug.checkDefined(buildResult));
|
|
147148
|
-
|
|
147148
|
+
step2++;
|
|
147149
147149
|
break;
|
|
147150
147150
|
// Should never be done
|
|
147151
147151
|
case 3:
|
|
147152
147152
|
default:
|
|
147153
|
-
assertType(
|
|
147153
|
+
assertType(step2);
|
|
147154
147154
|
}
|
|
147155
|
-
Debug.assert(
|
|
147155
|
+
Debug.assert(step2 > currentStep);
|
|
147156
147156
|
}
|
|
147157
147157
|
}
|
|
147158
147158
|
}
|
|
@@ -159531,12 +159531,12 @@ interface Symbol {
|
|
|
159531
159531
|
void 0,
|
|
159532
159532
|
typeParameter.name
|
|
159533
159533
|
);
|
|
159534
|
-
const
|
|
159534
|
+
const template = factory.createJSDocTemplateTag(
|
|
159535
159535
|
factory.createIdentifier("template"),
|
|
159536
159536
|
constraint && cast(constraint, isJSDocTypeExpression),
|
|
159537
159537
|
[parameter]
|
|
159538
159538
|
);
|
|
159539
|
-
templates.push(
|
|
159539
|
+
templates.push(template);
|
|
159540
159540
|
});
|
|
159541
159541
|
const jsDoc = factory.createJSDocComment(
|
|
159542
159542
|
/*comment*/
|
|
@@ -191190,11 +191190,11 @@ ${content}
|
|
|
191190
191190
|
return createTextSpan(applicableSpanStart, applicableSpanEnd - applicableSpanStart);
|
|
191191
191191
|
}
|
|
191192
191192
|
function getApplicableSpanForTaggedTemplate(taggedTemplate, sourceFile) {
|
|
191193
|
-
const
|
|
191194
|
-
const applicableSpanStart =
|
|
191195
|
-
let applicableSpanEnd =
|
|
191196
|
-
if (
|
|
191197
|
-
const lastSpan = last(
|
|
191193
|
+
const template = taggedTemplate.template;
|
|
191194
|
+
const applicableSpanStart = template.getStart();
|
|
191195
|
+
let applicableSpanEnd = template.getEnd();
|
|
191196
|
+
if (template.kind === 229) {
|
|
191197
|
+
const lastSpan = last(template.templateSpans);
|
|
191198
191198
|
if (lastSpan.literal.getFullWidth() === 0) {
|
|
191199
191199
|
applicableSpanEnd = skipTrivia(
|
|
191200
191200
|
sourceFile.text,
|
|
@@ -214431,8 +214431,119 @@ function formatError(e) {
|
|
|
214431
214431
|
// src/commands/init.ts
|
|
214432
214432
|
init_git();
|
|
214433
214433
|
var import_scanner = __toESM(require_dist());
|
|
214434
|
+
var import_node_fs4 = require("node:fs");
|
|
214435
|
+
var import_node_path4 = require("node:path");
|
|
214436
|
+
|
|
214437
|
+
// src/commands/install-skill.ts
|
|
214434
214438
|
var import_node_fs3 = require("node:fs");
|
|
214435
214439
|
var import_node_path3 = require("node:path");
|
|
214440
|
+
var SKILL_URL = "https://raw.githubusercontent.com/driftless-gh/driftless-skill/main/skills/driftless/SKILL.md";
|
|
214441
|
+
var AGENTS_BLOCK = `
|
|
214442
|
+
## Driftless \u2014 Context Integrity
|
|
214443
|
+
|
|
214444
|
+
Before touching files:
|
|
214445
|
+
driftless context push --files "<files you're about to edit>"
|
|
214446
|
+
|
|
214447
|
+
Before pushing:
|
|
214448
|
+
driftless scan --diff
|
|
214449
|
+
|
|
214450
|
+
Save discoveries:
|
|
214451
|
+
driftless context update <slug> --gotchas "..." --decisions "..."
|
|
214452
|
+
|
|
214453
|
+
Full skill \u2192 https://github.com/driftless-gh/driftless-skill
|
|
214454
|
+
`;
|
|
214455
|
+
async function downloadSkill(cwd) {
|
|
214456
|
+
const driftlessDir = (0, import_node_path3.resolve)(cwd, ".driftless");
|
|
214457
|
+
if (!(0, import_node_fs3.existsSync)(driftlessDir)) {
|
|
214458
|
+
(0, import_node_fs3.mkdirSync)(driftlessDir, { recursive: true });
|
|
214459
|
+
}
|
|
214460
|
+
const skillPath = (0, import_node_path3.resolve)(driftlessDir, "skill.md");
|
|
214461
|
+
try {
|
|
214462
|
+
const { default: https } = await import("node:https");
|
|
214463
|
+
const { default: http } = await import("node:http");
|
|
214464
|
+
const url = new URL(SKILL_URL);
|
|
214465
|
+
const client = url.protocol === "https:" ? https : http;
|
|
214466
|
+
await new Promise((ok, fail) => {
|
|
214467
|
+
client.get(SKILL_URL, (res) => {
|
|
214468
|
+
if (res.statusCode !== 200) {
|
|
214469
|
+
fail(new Error(`HTTP ${res.statusCode}`));
|
|
214470
|
+
return;
|
|
214471
|
+
}
|
|
214472
|
+
const chunks = [];
|
|
214473
|
+
res.on("data", (chunk) => chunks.push(chunk));
|
|
214474
|
+
res.on("end", () => {
|
|
214475
|
+
(0, import_node_fs3.writeFileSync)(skillPath, Buffer.concat(chunks), "utf-8");
|
|
214476
|
+
ok();
|
|
214477
|
+
});
|
|
214478
|
+
res.on("error", fail);
|
|
214479
|
+
}).on("error", fail);
|
|
214480
|
+
});
|
|
214481
|
+
} catch {
|
|
214482
|
+
(0, import_node_fs3.writeFileSync)(
|
|
214483
|
+
skillPath,
|
|
214484
|
+
`# Driftless Skill
|
|
214485
|
+
|
|
214486
|
+
See https://github.com/driftless-gh/driftless-skill for the full skill.
|
|
214487
|
+
`,
|
|
214488
|
+
"utf-8"
|
|
214489
|
+
);
|
|
214490
|
+
}
|
|
214491
|
+
}
|
|
214492
|
+
function handleClaudeMd(cwd) {
|
|
214493
|
+
const claudePath = (0, import_node_path3.resolve)(cwd, "CLAUDE.md");
|
|
214494
|
+
const reference = "@.driftless/skill.md";
|
|
214495
|
+
if (!(0, import_node_fs3.existsSync)(claudePath)) {
|
|
214496
|
+
(0, import_node_fs3.writeFileSync)(claudePath, `${reference}
|
|
214497
|
+
`, "utf-8");
|
|
214498
|
+
return "created";
|
|
214499
|
+
}
|
|
214500
|
+
const existing = (0, import_node_fs3.readFileSync)(claudePath, "utf-8");
|
|
214501
|
+
if (existing.includes(reference)) {
|
|
214502
|
+
return "already configured";
|
|
214503
|
+
}
|
|
214504
|
+
const needsNewline = existing.length > 0 && !existing.endsWith("\n");
|
|
214505
|
+
(0, import_node_fs3.writeFileSync)(claudePath, existing + (needsNewline ? "\n" : "") + `
|
|
214506
|
+
${reference}
|
|
214507
|
+
`, "utf-8");
|
|
214508
|
+
return "updated";
|
|
214509
|
+
}
|
|
214510
|
+
function handleAgentsMd(cwd) {
|
|
214511
|
+
const agentsPath = (0, import_node_path3.resolve)(cwd, "AGENTS.md");
|
|
214512
|
+
if (!(0, import_node_fs3.existsSync)(agentsPath)) {
|
|
214513
|
+
(0, import_node_fs3.writeFileSync)(agentsPath, AGENTS_BLOCK.trimStart(), "utf-8");
|
|
214514
|
+
return "created";
|
|
214515
|
+
}
|
|
214516
|
+
const existing = (0, import_node_fs3.readFileSync)(agentsPath, "utf-8");
|
|
214517
|
+
if (existing.includes("## Driftless")) {
|
|
214518
|
+
return "already configured";
|
|
214519
|
+
}
|
|
214520
|
+
const needsNewline = existing.length > 0 && !existing.endsWith("\n");
|
|
214521
|
+
(0, import_node_fs3.writeFileSync)(agentsPath, existing + (needsNewline ? "\n" : "") + AGENTS_BLOCK, "utf-8");
|
|
214522
|
+
return "updated";
|
|
214523
|
+
}
|
|
214524
|
+
async function runInstallSkill(cwd) {
|
|
214525
|
+
await downloadSkill(cwd);
|
|
214526
|
+
const claudeMd = handleClaudeMd(cwd);
|
|
214527
|
+
const agentsMd = handleAgentsMd(cwd);
|
|
214528
|
+
return { claudeMd, agentsMd };
|
|
214529
|
+
}
|
|
214530
|
+
async function installSkillCommand() {
|
|
214531
|
+
const cwd = process.cwd();
|
|
214532
|
+
const result = await runInstallSkill(cwd);
|
|
214533
|
+
const icon = (s) => s === "created" ? "created \u2713" : s === "updated" ? "updated \u2713" : "already configured \u2713";
|
|
214534
|
+
console.log(` CLAUDE.md ${icon(result.claudeMd)}`);
|
|
214535
|
+
console.log(` AGENTS.md ${icon(result.agentsMd)}`);
|
|
214536
|
+
return result;
|
|
214537
|
+
}
|
|
214538
|
+
|
|
214539
|
+
// src/commands/init.ts
|
|
214540
|
+
function getVersion() {
|
|
214541
|
+
try {
|
|
214542
|
+
return "0.1.26";
|
|
214543
|
+
} catch {
|
|
214544
|
+
return "0.0.0";
|
|
214545
|
+
}
|
|
214546
|
+
}
|
|
214436
214547
|
function analyzeCodePatterns(components) {
|
|
214437
214548
|
const pattern = {
|
|
214438
214549
|
guardNames: [],
|
|
@@ -214690,15 +214801,15 @@ function detectExistingDocs(cwd) {
|
|
|
214690
214801
|
{ path: "README.md", name: "readme", what: "Project overview and setup", pattern: void 0 }
|
|
214691
214802
|
];
|
|
214692
214803
|
for (const candidate of candidates) {
|
|
214693
|
-
const fullPath = (0,
|
|
214694
|
-
if ((0,
|
|
214804
|
+
const fullPath = (0, import_node_path4.resolve)(cwd, candidate.path);
|
|
214805
|
+
if ((0, import_node_fs4.existsSync)(fullPath)) {
|
|
214695
214806
|
docs.push({ path: candidate.path, name: candidate.name, what: candidate.what, pattern: candidate.pattern });
|
|
214696
214807
|
}
|
|
214697
214808
|
}
|
|
214698
|
-
const docsDir = (0,
|
|
214699
|
-
if ((0,
|
|
214809
|
+
const docsDir = (0, import_node_path4.resolve)(cwd, "docs");
|
|
214810
|
+
if ((0, import_node_fs4.existsSync)(docsDir)) {
|
|
214700
214811
|
try {
|
|
214701
|
-
const entries = (0,
|
|
214812
|
+
const entries = (0, import_node_fs4.readdirSync)(docsDir, { withFileTypes: true });
|
|
214702
214813
|
for (const entry of entries) {
|
|
214703
214814
|
if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
214704
214815
|
const existingPath = candidates.find((c) => c.path === `docs/${entry.name}`);
|
|
@@ -214718,8 +214829,23 @@ function detectExistingDocs(cwd) {
|
|
|
214718
214829
|
}
|
|
214719
214830
|
return docs;
|
|
214720
214831
|
}
|
|
214832
|
+
var DIVIDER = "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500";
|
|
214833
|
+
var DOT_TOTAL = 36;
|
|
214834
|
+
function step(label, value) {
|
|
214835
|
+
const prefix = ` \u2192 ${label}`;
|
|
214836
|
+
const dots = ".".repeat(Math.max(2, DOT_TOTAL - prefix.length));
|
|
214837
|
+
console.log(`${prefix}${dots} ${value}`);
|
|
214838
|
+
}
|
|
214721
214839
|
async function initCommand(args) {
|
|
214722
|
-
|
|
214840
|
+
const version = getVersion();
|
|
214841
|
+
console.log("D R I F T L E S S");
|
|
214842
|
+
console.log(`v${version}`);
|
|
214843
|
+
console.log("");
|
|
214844
|
+
console.log("AI ships fast. Humans lose context faster.");
|
|
214845
|
+
console.log("Driftless fixes that.");
|
|
214846
|
+
console.log("");
|
|
214847
|
+
console.log(DIVIDER);
|
|
214848
|
+
console.log("");
|
|
214723
214849
|
if (!isGitRepo()) {
|
|
214724
214850
|
console.error("Error: not a git repository.");
|
|
214725
214851
|
process.exit(1);
|
|
@@ -214729,8 +214855,6 @@ async function initCommand(args) {
|
|
|
214729
214855
|
console.error("Error: no git remote found.");
|
|
214730
214856
|
process.exit(1);
|
|
214731
214857
|
}
|
|
214732
|
-
console.log(`Repository: ${remote.org}/${remote.repo}`);
|
|
214733
|
-
console.log("\nConnecting to Driftless Cloud...");
|
|
214734
214858
|
let workspace;
|
|
214735
214859
|
try {
|
|
214736
214860
|
workspace = await api.get("/me");
|
|
@@ -214738,15 +214862,18 @@ async function initCommand(args) {
|
|
|
214738
214862
|
console.error("Could not resolve workspace. Check your API key.");
|
|
214739
214863
|
process.exit(1);
|
|
214740
214864
|
}
|
|
214741
|
-
console.log(` Workspace: ${workspace.slug} \u2713`);
|
|
214742
214865
|
} catch (err) {
|
|
214743
|
-
console.error(`
|
|
214866
|
+
console.error(`Failed to connect: ${err?.message || "unreachable"}`);
|
|
214744
214867
|
process.exit(1);
|
|
214745
214868
|
}
|
|
214746
214869
|
const workspaceSlug = workspace.slug;
|
|
214747
|
-
console.log(
|
|
214870
|
+
console.log(`Repository: ${remote.org}/${remote.repo}`);
|
|
214871
|
+
console.log(`Workspace: ${workspaceSlug} \u2713`);
|
|
214872
|
+
console.log("");
|
|
214748
214873
|
const cwd = process.cwd();
|
|
214874
|
+
const scanStart = Date.now();
|
|
214749
214875
|
const scanResult = await (0, import_scanner.scanRepo)(cwd);
|
|
214876
|
+
const scanMs = Date.now() - scanStart;
|
|
214750
214877
|
const earlyPatterns = analyzeCodePatterns(scanResult.components);
|
|
214751
214878
|
const summary = {
|
|
214752
214879
|
endpoints: scanResult.stats.total_endpoints,
|
|
@@ -214773,36 +214900,31 @@ async function initCommand(args) {
|
|
|
214773
214900
|
const actualServices = components.filter((c) => c.type === "service").length;
|
|
214774
214901
|
const actualModules = components.filter((c) => c.type === "module").length;
|
|
214775
214902
|
const actualGuards = components.filter((c) => c.type === "guard").length;
|
|
214776
|
-
const
|
|
214777
|
-
|
|
214778
|
-
|
|
214779
|
-
|
|
214780
|
-
|
|
214781
|
-
|
|
214782
|
-
|
|
214783
|
-
if (
|
|
214784
|
-
console.log(`
|
|
214785
|
-
}
|
|
214786
|
-
|
|
214787
|
-
const t = scanResult.telemetry;
|
|
214788
|
-
console.log(` Scan: ${t.duration_ms}ms, ${t.memory_mb}MB heap, ${t.files_parsed} files`);
|
|
214903
|
+
const frameworkLabel = [summary.framework, "ts"].filter(Boolean).join(", ");
|
|
214904
|
+
step("detecting framework", frameworkLabel);
|
|
214905
|
+
step("extracting components", `${components.length} found`);
|
|
214906
|
+
step("mapping relations", `${relationCount} mapped`);
|
|
214907
|
+
const patternsLabel = `${actualGuards} guard${actualGuards !== 1 ? "s" : ""} \xB7 ${actualEndpoints} endpoints`;
|
|
214908
|
+
step("analyzing patterns", patternsLabel);
|
|
214909
|
+
const telemetry = scanResult.telemetry;
|
|
214910
|
+
if (telemetry) {
|
|
214911
|
+
console.log(` Scan: ${telemetry.duration_ms}ms \xB7 ${telemetry.memory_mb}MB \xB7 ${telemetry.files_parsed} files`);
|
|
214912
|
+
} else {
|
|
214913
|
+
console.log(` Scan: ${scanMs}ms`);
|
|
214789
214914
|
}
|
|
214915
|
+
console.log("");
|
|
214790
214916
|
let repo;
|
|
214791
214917
|
try {
|
|
214792
214918
|
const repos = await api.get(`/workspaces/${workspaceSlug}/repos`);
|
|
214793
214919
|
repo = Array.isArray(repos) ? repos.find((r) => r.github_org === remote.org && r.github_repo === remote.repo) : null;
|
|
214794
214920
|
} catch {
|
|
214795
214921
|
}
|
|
214796
|
-
if (repo) {
|
|
214797
|
-
console.log(`
|
|
214798
|
-
Repo: ${remote.repo} \u2713`);
|
|
214799
|
-
} else {
|
|
214922
|
+
if (!repo) {
|
|
214800
214923
|
try {
|
|
214801
214924
|
repo = await api.post(`/workspaces/${workspaceSlug}/repos`, {
|
|
214802
214925
|
github_org: remote.org,
|
|
214803
214926
|
github_repo: remote.repo
|
|
214804
214927
|
});
|
|
214805
|
-
console.log(` Repo: ${remote.repo} (connected)`);
|
|
214806
214928
|
} catch (err) {
|
|
214807
214929
|
console.error(` Failed to connect repo: ${err?.message || "unknown"}`);
|
|
214808
214930
|
process.exit(1);
|
|
@@ -214810,39 +214932,35 @@ async function initCommand(args) {
|
|
|
214810
214932
|
}
|
|
214811
214933
|
try {
|
|
214812
214934
|
await api.post(`/workspaces/${workspaceSlug}/repos/${repo.id}/baseline`, summary);
|
|
214813
|
-
|
|
214814
|
-
} catch
|
|
214815
|
-
|
|
214935
|
+
step("uploading baseline", "\u2713");
|
|
214936
|
+
} catch {
|
|
214937
|
+
step("uploading baseline", "failed (continuing)");
|
|
214816
214938
|
}
|
|
214817
214939
|
if (components.length > 0) {
|
|
214818
214940
|
try {
|
|
214819
214941
|
await api.post(`/workspaces/${workspaceSlug}/repos/${repo.id}/components`, { components });
|
|
214820
|
-
|
|
214821
|
-
} catch
|
|
214822
|
-
|
|
214942
|
+
step("uploading components", `${components.length} components \xB7 ${relationCount} relations \u2713`);
|
|
214943
|
+
} catch {
|
|
214944
|
+
step("uploading components", "failed (continuing)");
|
|
214823
214945
|
}
|
|
214824
214946
|
}
|
|
214825
|
-
console.log("\nAnalyzing code patterns...");
|
|
214826
214947
|
const patterns = earlyPatterns;
|
|
214827
214948
|
const smartRules = generateSmartRules(repo.id, patterns);
|
|
214828
|
-
console.log(` Detected: ${patterns.guardNames.length} guards, ${patterns.endpointPaths.length} endpoints`);
|
|
214829
|
-
if (patterns.hasMultiTenant) console.log(" Multi-tenant pattern detected");
|
|
214830
|
-
if (patterns.largeFiles.length > 0) console.log(` Large files: ${patterns.largeFiles.length} (quarantine candidates)`);
|
|
214831
|
-
if (patterns.quarantineCandidates.length > 0) console.log(` Legacy files: ${patterns.quarantineCandidates.length}`);
|
|
214832
|
-
console.log(`
|
|
214833
|
-
Creating ${smartRules.length} architectural rules...`);
|
|
214834
214949
|
let rulesCreated = 0;
|
|
214835
214950
|
let rulesSkipped = 0;
|
|
214836
214951
|
for (const rule of smartRules) {
|
|
214837
214952
|
try {
|
|
214838
|
-
await api.post(`/workspaces/${workspaceSlug}/rules`, { ...rule, auto_suggested: true });
|
|
214839
|
-
|
|
214953
|
+
const result = await api.post(`/workspaces/${workspaceSlug}/rules`, { ...rule, auto_suggested: true });
|
|
214954
|
+
if (result?.skipped) {
|
|
214955
|
+
rulesSkipped++;
|
|
214956
|
+
} else {
|
|
214957
|
+
rulesCreated++;
|
|
214958
|
+
}
|
|
214840
214959
|
} catch {
|
|
214841
214960
|
rulesSkipped++;
|
|
214842
214961
|
}
|
|
214843
214962
|
}
|
|
214844
|
-
|
|
214845
|
-
console.log("\nCreating context watchers from detected modules...");
|
|
214963
|
+
step("creating rules", `${rulesCreated} created \xB7 ${rulesSkipped} skipped \u2713`);
|
|
214846
214964
|
const smartWatchers = generateSmartWatchers(components);
|
|
214847
214965
|
let watchersCreated = 0;
|
|
214848
214966
|
let watchersSkipped = 0;
|
|
@@ -214860,19 +214978,17 @@ Creating ${smartRules.length} architectural rules...`);
|
|
|
214860
214978
|
created_by: "driftless-init"
|
|
214861
214979
|
});
|
|
214862
214980
|
watchersCreated++;
|
|
214863
|
-
console.log(` \u2713 ${watcher.name} \u2014 ${watcher.what}`);
|
|
214864
214981
|
} catch {
|
|
214865
214982
|
watchersSkipped++;
|
|
214866
214983
|
}
|
|
214867
214984
|
}
|
|
214868
|
-
|
|
214869
|
-
console.log("\nScanning for existing documentation...");
|
|
214985
|
+
step("creating context", `${watchersCreated} watchers \u2713`);
|
|
214870
214986
|
const existingDocs = detectExistingDocs(cwd);
|
|
214871
214987
|
let docsAnchored = 0;
|
|
214872
214988
|
for (const doc of existingDocs) {
|
|
214873
214989
|
try {
|
|
214874
|
-
const fullPath = (0,
|
|
214875
|
-
const fileContent = (0,
|
|
214990
|
+
const fullPath = (0, import_node_path4.resolve)(cwd, doc.path);
|
|
214991
|
+
const fileContent = (0, import_node_fs4.readFileSync)(fullPath, "utf-8");
|
|
214876
214992
|
await api.post(`/workspaces/${workspaceSlug}/watchers`, {
|
|
214877
214993
|
name: doc.name,
|
|
214878
214994
|
what: doc.what,
|
|
@@ -214885,21 +215001,31 @@ Creating ${smartRules.length} architectural rules...`);
|
|
|
214885
215001
|
created_by: "driftless-init"
|
|
214886
215002
|
});
|
|
214887
215003
|
docsAnchored++;
|
|
214888
|
-
console.log(` \u2713 ${doc.name} \u2014 ${doc.path}`);
|
|
214889
215004
|
} catch {
|
|
214890
215005
|
}
|
|
214891
215006
|
}
|
|
214892
|
-
|
|
214893
|
-
|
|
214894
|
-
|
|
214895
|
-
|
|
214896
|
-
|
|
214897
|
-
|
|
214898
|
-
|
|
214899
|
-
|
|
214900
|
-
|
|
214901
|
-
|
|
214902
|
-
console.log(
|
|
215007
|
+
step("anchoring docs", `${docsAnchored} docs \u2713`);
|
|
215008
|
+
let skillLine = "skipped";
|
|
215009
|
+
try {
|
|
215010
|
+
const skillResult = await runInstallSkill(cwd);
|
|
215011
|
+
const claudeIcon = skillResult.claudeMd === "created" ? "CLAUDE.md created" : skillResult.claudeMd === "updated" ? "CLAUDE.md updated" : "CLAUDE.md ok";
|
|
215012
|
+
const agentsIcon = skillResult.agentsMd === "created" ? "AGENTS.md created" : skillResult.agentsMd === "updated" ? "AGENTS.md updated" : "AGENTS.md ok";
|
|
215013
|
+
skillLine = `${claudeIcon} \xB7 ${agentsIcon} \u2713`;
|
|
215014
|
+
} catch {
|
|
215015
|
+
}
|
|
215016
|
+
step("installing skill", skillLine);
|
|
215017
|
+
console.log("");
|
|
215018
|
+
console.log("\u2713 repo context bootstrapped");
|
|
215019
|
+
console.log(` \u251C\u2500 watchers ${watchersCreated}`);
|
|
215020
|
+
console.log(` \u251C\u2500 rules ${rulesCreated}`);
|
|
215021
|
+
console.log(` \u251C\u2500 docs ${docsAnchored}`);
|
|
215022
|
+
console.log(` \u251C\u2500 components ${components.length}`);
|
|
215023
|
+
console.log(` \u2514\u2500 relations ${relationCount}`);
|
|
215024
|
+
console.log("");
|
|
215025
|
+
console.log(" dashboard \u2192 driftless.icu/ecosystem");
|
|
215026
|
+
console.log(" next \u2192 driftless context list");
|
|
215027
|
+
console.log("");
|
|
215028
|
+
console.log(DIVIDER);
|
|
214903
215029
|
}
|
|
214904
215030
|
|
|
214905
215031
|
// src/commands/scan.ts
|
|
@@ -215015,8 +215141,8 @@ ${result.violations.length} violation(s) found (${rulesEvaluated} rule(s) evalua
|
|
|
215015
215141
|
|
|
215016
215142
|
// src/commands/context.ts
|
|
215017
215143
|
init_git();
|
|
215018
|
-
var
|
|
215019
|
-
var
|
|
215144
|
+
var import_node_fs5 = require("node:fs");
|
|
215145
|
+
var import_node_path5 = require("node:path");
|
|
215020
215146
|
function parseArgs(args) {
|
|
215021
215147
|
const flags = {};
|
|
215022
215148
|
const positional = [];
|
|
@@ -215072,6 +215198,7 @@ ${items.length} topic${items.length === 1 ? "" : "s"}.`);
|
|
|
215072
215198
|
function renderContextHuman(ctx) {
|
|
215073
215199
|
console.log(`\u258C ${ctx.topic}`);
|
|
215074
215200
|
console.log(` ${ctx.summary}`);
|
|
215201
|
+
if (ctx.version !== void 0) console.log(` version: ${ctx.version}`);
|
|
215075
215202
|
if (ctx.stale.is_stale) {
|
|
215076
215203
|
console.log(`
|
|
215077
215204
|
\u26A0 STALE: ${ctx.stale.reason || "Context may be outdated"}`);
|
|
@@ -215249,7 +215376,7 @@ async function contextCommand(args) {
|
|
|
215249
215376
|
}
|
|
215250
215377
|
process.exit(0);
|
|
215251
215378
|
}
|
|
215252
|
-
const missingFiles = files.filter((f) => !(0,
|
|
215379
|
+
const missingFiles = files.filter((f) => !(0, import_node_fs5.existsSync)((0, import_node_path5.resolve)(process.cwd(), f)));
|
|
215253
215380
|
if (missingFiles.length > 0 && !isJSON) {
|
|
215254
215381
|
console.error(`File(s) not found locally: ${missingFiles.join(", ")}. Matching by pattern only.`);
|
|
215255
215382
|
}
|
|
@@ -215328,7 +215455,7 @@ async function contextCommand(args) {
|
|
|
215328
215455
|
let fileContent;
|
|
215329
215456
|
if (flags["file"]) {
|
|
215330
215457
|
try {
|
|
215331
|
-
fileContent = (0,
|
|
215458
|
+
fileContent = (0, import_node_fs5.readFileSync)(flags["file"], "utf-8");
|
|
215332
215459
|
} catch {
|
|
215333
215460
|
console.error(`File not found: ${flags["file"]}`);
|
|
215334
215461
|
process.exit(1);
|
|
@@ -215493,7 +215620,7 @@ async function contextCommand(args) {
|
|
|
215493
215620
|
let fileContent;
|
|
215494
215621
|
if (docFlag) {
|
|
215495
215622
|
try {
|
|
215496
|
-
fileContent = (0,
|
|
215623
|
+
fileContent = (0, import_node_fs5.readFileSync)(docFlag, "utf-8");
|
|
215497
215624
|
} catch {
|
|
215498
215625
|
console.error(`File not found: ${docFlag}`);
|
|
215499
215626
|
process.exit(1);
|
|
@@ -215555,7 +215682,7 @@ async function contextCommand(args) {
|
|
|
215555
215682
|
console.error("No files provided.");
|
|
215556
215683
|
process.exit(1);
|
|
215557
215684
|
}
|
|
215558
|
-
const missingFiles = files.filter((f) => !(0,
|
|
215685
|
+
const missingFiles = files.filter((f) => !(0, import_node_fs5.existsSync)((0, import_node_path5.resolve)(process.cwd(), f)));
|
|
215559
215686
|
if (missingFiles.length > 0 && !isJSON) {
|
|
215560
215687
|
console.error(`File(s) not found locally: ${missingFiles.join(", ")}. Matching by pattern only.`);
|
|
215561
215688
|
}
|
|
@@ -215600,14 +215727,21 @@ Warnings:`);
|
|
|
215600
215727
|
emitJSON2(results);
|
|
215601
215728
|
} else {
|
|
215602
215729
|
if (results.length > 0) {
|
|
215603
|
-
console.log(`Matched ${results.length} topic${results.length === 1 ? "" : "s"}
|
|
215730
|
+
console.log(`Matched ${results.length} context topic${results.length === 1 ? "" : "s"} for ${files.length} file${files.length === 1 ? "" : "s"}:
|
|
215731
|
+
`);
|
|
215604
215732
|
for (const r of results) {
|
|
215605
|
-
|
|
215733
|
+
try {
|
|
215734
|
+
const fullCtx = await api.get(`/workspaces/${workspaceSlug}/watchers/${r.context.topic}`);
|
|
215735
|
+
renderContextHuman(fullCtx);
|
|
215736
|
+
console.log("");
|
|
215737
|
+
} catch {
|
|
215738
|
+
console.log(`\u258C ${r.context.topic} (${r.match_reason})`);
|
|
215739
|
+
console.log(` ${r.context.summary}`);
|
|
215740
|
+
console.log("");
|
|
215741
|
+
}
|
|
215606
215742
|
}
|
|
215607
|
-
console.log(`
|
|
215608
|
-
Context delivered for ${files.length} file${files.length === 1 ? "" : "s"}.`);
|
|
215609
215743
|
} else {
|
|
215610
|
-
console.log(`No context topics match these files. No
|
|
215744
|
+
console.log(`No context topics match these files. No context covers the changed paths.`);
|
|
215611
215745
|
}
|
|
215612
215746
|
}
|
|
215613
215747
|
} catch (e) {
|
|
@@ -215616,6 +215750,157 @@ Context delivered for ${files.length} file${files.length === 1 ? "" : "s"}.`);
|
|
|
215616
215750
|
}
|
|
215617
215751
|
return;
|
|
215618
215752
|
}
|
|
215753
|
+
if (subCommand === "export") {
|
|
215754
|
+
const dir = flags["dir"] ?? ".driftless/watchers";
|
|
215755
|
+
const absDir = (0, import_node_path5.resolve)(process.cwd(), dir);
|
|
215756
|
+
try {
|
|
215757
|
+
const summaries = await api.get(`/workspaces/${workspaceSlug}/watchers`);
|
|
215758
|
+
(0, import_node_fs5.mkdirSync)(absDir, { recursive: true });
|
|
215759
|
+
for (const summary of summaries) {
|
|
215760
|
+
const slug = summary.topic;
|
|
215761
|
+
let ctx;
|
|
215762
|
+
try {
|
|
215763
|
+
ctx = await api.get(`/workspaces/${workspaceSlug}/watchers/${slug}`);
|
|
215764
|
+
} catch {
|
|
215765
|
+
console.error(` skipped ${slug} (fetch failed)`);
|
|
215766
|
+
continue;
|
|
215767
|
+
}
|
|
215768
|
+
const lines = [];
|
|
215769
|
+
const needsQuotes = (s) => /[:#\[\]{}&*!|>'"%@`,]/.test(s) || s.trim() !== s;
|
|
215770
|
+
const safeStr = (s) => needsQuotes(s) ? `"${s.replace(/"/g, '\\"')}"` : s;
|
|
215771
|
+
lines.push(`slug: ${safeStr(slug)}`);
|
|
215772
|
+
if (ctx.version !== void 0) lines.push(`version: ${ctx.version}`);
|
|
215773
|
+
if (ctx.description.what) lines.push(`what: ${safeStr(ctx.description.what)}`);
|
|
215774
|
+
if (ctx.description.how) lines.push(`how: ${safeStr(ctx.description.how)}`);
|
|
215775
|
+
if (ctx.anchors.files.length > 0) {
|
|
215776
|
+
lines.push(`where_files:`);
|
|
215777
|
+
for (const f of ctx.anchors.files) lines.push(` - ${safeStr(f)}`);
|
|
215778
|
+
}
|
|
215779
|
+
if (ctx.anchors.docs.length > 0 && ctx.anchors.docs[0].path) {
|
|
215780
|
+
lines.push(`anchored_doc: ${safeStr(ctx.anchors.docs[0].path)}`);
|
|
215781
|
+
}
|
|
215782
|
+
if (ctx.description.decisions) lines.push(`decisions: ${safeStr(ctx.description.decisions)}`);
|
|
215783
|
+
if (ctx.description.gotchas.length > 0) {
|
|
215784
|
+
lines.push(`gotchas: ${safeStr(ctx.description.gotchas.join(". "))}`);
|
|
215785
|
+
}
|
|
215786
|
+
if (ctx.description.ownership) lines.push(`ownership: ${safeStr(ctx.description.ownership)}`);
|
|
215787
|
+
const yamlContent = lines.join("\n") + "\n";
|
|
215788
|
+
(0, import_node_fs5.writeFileSync)((0, import_node_path5.join)(absDir, `${slug}.yaml`), yamlContent, "utf-8");
|
|
215789
|
+
}
|
|
215790
|
+
if (!isJSON) {
|
|
215791
|
+
console.log(`Exported ${summaries.length} context topic${summaries.length === 1 ? "" : "s"} \u2192 ${dir}/`);
|
|
215792
|
+
} else {
|
|
215793
|
+
emitJSON2({ exported: summaries.length, dir });
|
|
215794
|
+
}
|
|
215795
|
+
} catch (e) {
|
|
215796
|
+
console.error(`Export failed: ${formatError(e)}`);
|
|
215797
|
+
process.exit(1);
|
|
215798
|
+
}
|
|
215799
|
+
return;
|
|
215800
|
+
}
|
|
215801
|
+
if (subCommand === "import") {
|
|
215802
|
+
const dir = flags["dir"] ?? ".driftless/watchers";
|
|
215803
|
+
const absDir = (0, import_node_path5.resolve)(process.cwd(), dir);
|
|
215804
|
+
if (!(0, import_node_fs5.existsSync)(absDir)) {
|
|
215805
|
+
console.error(`Directory not found: ${dir}`);
|
|
215806
|
+
process.exit(1);
|
|
215807
|
+
}
|
|
215808
|
+
let files;
|
|
215809
|
+
try {
|
|
215810
|
+
files = (0, import_node_fs5.readdirSync)(absDir).filter((f) => f.endsWith(".yaml"));
|
|
215811
|
+
} catch (e) {
|
|
215812
|
+
console.error(`Failed to read directory: ${formatError(e)}`);
|
|
215813
|
+
process.exit(1);
|
|
215814
|
+
}
|
|
215815
|
+
if (files.length === 0) {
|
|
215816
|
+
console.log(`No .yaml files found in ${dir}/`);
|
|
215817
|
+
return;
|
|
215818
|
+
}
|
|
215819
|
+
let created = 0;
|
|
215820
|
+
let updated = 0;
|
|
215821
|
+
for (const file of files) {
|
|
215822
|
+
const filePath = (0, import_node_path5.join)(absDir, file);
|
|
215823
|
+
let raw;
|
|
215824
|
+
try {
|
|
215825
|
+
raw = (0, import_node_fs5.readFileSync)(filePath, "utf-8");
|
|
215826
|
+
} catch {
|
|
215827
|
+
console.error(` skipped ${file} (read error)`);
|
|
215828
|
+
continue;
|
|
215829
|
+
}
|
|
215830
|
+
const parsed = {};
|
|
215831
|
+
let lastArrayKey = null;
|
|
215832
|
+
for (const line of raw.split("\n")) {
|
|
215833
|
+
if (!line.trim() || line.trim().startsWith("#")) continue;
|
|
215834
|
+
if (/^\s{2,}-\s/.test(line)) {
|
|
215835
|
+
if (lastArrayKey) {
|
|
215836
|
+
const item = line.replace(/^\s{2,}-\s*/, "").trim();
|
|
215837
|
+
const existing = parsed[lastArrayKey];
|
|
215838
|
+
if (Array.isArray(existing)) {
|
|
215839
|
+
existing.push(item);
|
|
215840
|
+
} else {
|
|
215841
|
+
parsed[lastArrayKey] = [item];
|
|
215842
|
+
}
|
|
215843
|
+
}
|
|
215844
|
+
continue;
|
|
215845
|
+
}
|
|
215846
|
+
const colonIdx = line.indexOf(":");
|
|
215847
|
+
if (colonIdx === -1) continue;
|
|
215848
|
+
const key = line.slice(0, colonIdx).trim();
|
|
215849
|
+
let value = line.slice(colonIdx + 1).trim();
|
|
215850
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
215851
|
+
value = value.slice(1, -1).replace(/\\"/g, '"');
|
|
215852
|
+
}
|
|
215853
|
+
if (value === "") {
|
|
215854
|
+
lastArrayKey = key;
|
|
215855
|
+
parsed[key] = [];
|
|
215856
|
+
} else {
|
|
215857
|
+
lastArrayKey = null;
|
|
215858
|
+
parsed[key] = value;
|
|
215859
|
+
}
|
|
215860
|
+
}
|
|
215861
|
+
const slug = parsed["slug"];
|
|
215862
|
+
if (!slug) {
|
|
215863
|
+
console.error(` skipped ${file} (no slug field)`);
|
|
215864
|
+
continue;
|
|
215865
|
+
}
|
|
215866
|
+
const payload = {};
|
|
215867
|
+
if (parsed["what"]) payload.what = parsed["what"];
|
|
215868
|
+
if (parsed["how"]) payload.how = parsed["how"];
|
|
215869
|
+
if (parsed["where_files"]) payload.where_files = parsed["where_files"];
|
|
215870
|
+
if (parsed["anchored_doc"]) payload.anchored_doc_path = parsed["anchored_doc"];
|
|
215871
|
+
if (parsed["decisions"]) payload.decisions = parsed["decisions"];
|
|
215872
|
+
if (parsed["gotchas"]) payload.gotchas = parsed["gotchas"];
|
|
215873
|
+
if (parsed["ownership"]) payload.ownership = parsed["ownership"];
|
|
215874
|
+
let exists = false;
|
|
215875
|
+
try {
|
|
215876
|
+
await api.get(`/workspaces/${workspaceSlug}/watchers/${slug}`);
|
|
215877
|
+
exists = true;
|
|
215878
|
+
} catch {
|
|
215879
|
+
exists = false;
|
|
215880
|
+
}
|
|
215881
|
+
try {
|
|
215882
|
+
if (exists) {
|
|
215883
|
+
await api.patch(`/workspaces/${workspaceSlug}/watchers/${slug}`, payload);
|
|
215884
|
+
console.log(` \u2713 ${slug} (updated)`);
|
|
215885
|
+
updated++;
|
|
215886
|
+
} else {
|
|
215887
|
+
const name = slug.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
215888
|
+
await api.post(`/workspaces/${workspaceSlug}/watchers`, { name, ...payload });
|
|
215889
|
+
console.log(` \u2713 ${slug} (created)`);
|
|
215890
|
+
created++;
|
|
215891
|
+
}
|
|
215892
|
+
} catch (e) {
|
|
215893
|
+
console.error(` \u2717 ${slug}: ${formatError(e)}`);
|
|
215894
|
+
}
|
|
215895
|
+
}
|
|
215896
|
+
if (!isJSON) {
|
|
215897
|
+
console.log(`
|
|
215898
|
+
Done: ${created} created, ${updated} updated.`);
|
|
215899
|
+
} else {
|
|
215900
|
+
emitJSON2({ created, updated });
|
|
215901
|
+
}
|
|
215902
|
+
return;
|
|
215903
|
+
}
|
|
215619
215904
|
console.log(`Usage: driftless context <list|get|add|update|delete|search|push> [args]
|
|
215620
215905
|
|
|
215621
215906
|
Run 'driftless help context' for full reference.`);
|
|
@@ -215623,8 +215908,8 @@ Run 'driftless help context' for full reference.`);
|
|
|
215623
215908
|
|
|
215624
215909
|
// src/commands/session.ts
|
|
215625
215910
|
init_git();
|
|
215626
|
-
var
|
|
215627
|
-
var
|
|
215911
|
+
var import_node_fs6 = require("node:fs");
|
|
215912
|
+
var import_node_path6 = require("node:path");
|
|
215628
215913
|
function parseArgs2(args) {
|
|
215629
215914
|
const flags = {};
|
|
215630
215915
|
const positional = [];
|
|
@@ -215686,7 +215971,7 @@ async function sessionCommand(args) {
|
|
|
215686
215971
|
}
|
|
215687
215972
|
process.exit(0);
|
|
215688
215973
|
}
|
|
215689
|
-
const missingFiles = files.filter((f) => !(0,
|
|
215974
|
+
const missingFiles = files.filter((f) => !(0, import_node_fs6.existsSync)((0, import_node_path6.resolve)(process.cwd(), f)));
|
|
215690
215975
|
if (missingFiles.length > 0 && !isJSON) {
|
|
215691
215976
|
console.error(`Warning: ${missingFiles.length} file(s) not found locally \u2014 matching by pattern only.`);
|
|
215692
215977
|
}
|
|
@@ -215735,7 +216020,7 @@ async function sessionCommand(args) {
|
|
|
215735
216020
|
console.log(`Session started for ${files.length} file(s):
|
|
215736
216021
|
`);
|
|
215737
216022
|
for (const f of files) {
|
|
215738
|
-
const exists = (0,
|
|
216023
|
+
const exists = (0, import_node_fs6.existsSync)((0, import_node_path6.resolve)(process.cwd(), f));
|
|
215739
216024
|
console.log(` ${exists ? "\u2713" : "\u2717"} ${f}`);
|
|
215740
216025
|
}
|
|
215741
216026
|
if (missingFiles.length > 0) {
|
|
@@ -215804,9 +216089,9 @@ Rules that will be evaluated:`);
|
|
|
215804
216089
|
console.error(`Repo '${remote.repo}' not found. Run 'driftless init' first.`);
|
|
215805
216090
|
process.exit(1);
|
|
215806
216091
|
}
|
|
215807
|
-
const { getUncommittedDiff:
|
|
216092
|
+
const { getUncommittedDiff: getUncommittedDiff2, getStagedDiff: getStagedDiff2, getLastCommitHash: getLastCommitHash2, getAuthorName: getAuthorName2 } = await Promise.resolve().then(() => (init_git(), git_exports));
|
|
215808
216093
|
const staged = getStagedDiff2();
|
|
215809
|
-
const unstaged =
|
|
216094
|
+
const unstaged = getUncommittedDiff2();
|
|
215810
216095
|
const diff = [staged, unstaged].filter(Boolean).join("\n");
|
|
215811
216096
|
if (!diff && !isJSON) {
|
|
215812
216097
|
console.log("No changes to scan. Session clean.");
|
|
@@ -215896,149 +216181,6 @@ Examples:
|
|
|
215896
216181
|
driftless session finish # scan + context report`);
|
|
215897
216182
|
}
|
|
215898
216183
|
|
|
215899
|
-
// src/commands/install-skill.ts
|
|
215900
|
-
var import_node_fs6 = require("node:fs");
|
|
215901
|
-
var import_node_path6 = require("node:path");
|
|
215902
|
-
var template = `# Driftless \u2014 Live Repo Context
|
|
215903
|
-
|
|
215904
|
-
Driftless Cloud holds the team's living codebase context (topics, anchors, rules, gotchas).
|
|
215905
|
-
This file teaches you how to use it. Treat it as part of the toolchain, not documentation.
|
|
215906
|
-
|
|
215907
|
-
## Agent loop
|
|
215908
|
-
|
|
215909
|
-
### 1. Before editing \u2014 pull context
|
|
215910
|
-
|
|
215911
|
-
If you know the topic:
|
|
215912
|
-
|
|
215913
|
-
\`\`\`bash
|
|
215914
|
-
driftless context get <topic>
|
|
215915
|
-
\`\`\`
|
|
215916
|
-
|
|
215917
|
-
If you don't know which topic applies, discover it:
|
|
215918
|
-
|
|
215919
|
-
\`\`\`bash
|
|
215920
|
-
driftless context search "<keyword>"
|
|
215921
|
-
driftless context list
|
|
215922
|
-
\`\`\`
|
|
215923
|
-
|
|
215924
|
-
The \`get\` response is a canonical JSON object:
|
|
215925
|
-
|
|
215926
|
-
\`\`\`json
|
|
215927
|
-
{
|
|
215928
|
-
"topic": "<slug>",
|
|
215929
|
-
"summary": "...",
|
|
215930
|
-
"description": {
|
|
215931
|
-
"what": "...",
|
|
215932
|
-
"how": "...",
|
|
215933
|
-
"decisions": "...",
|
|
215934
|
-
"gotchas": [],
|
|
215935
|
-
"ownership": "..."
|
|
215936
|
-
},
|
|
215937
|
-
"anchors": {
|
|
215938
|
-
"files": [],
|
|
215939
|
-
"patterns": [],
|
|
215940
|
-
"repos": [],
|
|
215941
|
-
"docs": []
|
|
215942
|
-
},
|
|
215943
|
-
"components": [],
|
|
215944
|
-
"rules": [],
|
|
215945
|
-
"invariants": [],
|
|
215946
|
-
"violations": [],
|
|
215947
|
-
"events": [],
|
|
215948
|
-
"required_checks": [],
|
|
215949
|
-
"stale": { "is_stale": false, "reason": null },
|
|
215950
|
-
"metadata": { "created_by": "...", "last_updated": "...", "source": "manual|auto|doc|agent" }
|
|
215951
|
-
}
|
|
215952
|
-
\`\`\`
|
|
215953
|
-
|
|
215954
|
-
Always read:
|
|
215955
|
-
- \`description.what\` and \`description.how\` \u2014 what you're about to touch
|
|
215956
|
-
- \`description.decisions\` \u2014 why it's built this way
|
|
215957
|
-
- \`description.gotchas\` \u2014 known traps
|
|
215958
|
-
- \`anchors.docs\` \u2014 anchored team docs
|
|
215959
|
-
- \`stale.is_stale\` \u2014 if true, the context may be outdated; read \`stale.reason\`
|
|
215960
|
-
|
|
215961
|
-
Note: \`rules\`, \`invariants\`, \`violations\`, \`events\`, and \`required_checks\` may be empty
|
|
215962
|
-
during PR 1; they get populated in subsequent releases. Other fields are authoritative.
|
|
215963
|
-
|
|
215964
|
-
### 2. During work
|
|
215965
|
-
|
|
215966
|
-
Respect what the topic tells you. Follow decisions and gotchas. If anchored docs contradict
|
|
215967
|
-
code, the docs are usually intent \u2014 verify before changing.
|
|
215968
|
-
|
|
215969
|
-
### 3. Before finishing \u2014 scan
|
|
215970
|
-
|
|
215971
|
-
\`\`\`bash
|
|
215972
|
-
driftless scan --diff
|
|
215973
|
-
\`\`\`
|
|
215974
|
-
|
|
215975
|
-
This evaluates your uncommitted changes against active Cloud rules. Exit code:
|
|
215976
|
-
- \`0\` \u2014 clean
|
|
215977
|
-
- \`1\` \u2014 violations (read each \`explanation\`, fix, re-scan until clean)
|
|
215978
|
-
|
|
215979
|
-
Do not push if scan returns violations.
|
|
215980
|
-
|
|
215981
|
-
### 4. After learning \u2014 write context back
|
|
215982
|
-
|
|
215983
|
-
If you discovered something durable about the codebase, write it back so the next session benefits:
|
|
215984
|
-
|
|
215985
|
-
Update an existing topic:
|
|
215986
|
-
|
|
215987
|
-
\`\`\`bash
|
|
215988
|
-
driftless context update <topic> \\
|
|
215989
|
-
--gotchas "Setting X to Y breaks Z under condition W"
|
|
215990
|
-
\`\`\`
|
|
215991
|
-
|
|
215992
|
-
Or add a new topic:
|
|
215993
|
-
|
|
215994
|
-
\`\`\`bash
|
|
215995
|
-
driftless context add "<name>" \\
|
|
215996
|
-
--what "What this piece is" \\
|
|
215997
|
-
--how "How it works" \\
|
|
215998
|
-
--pattern "src/path/**" \\
|
|
215999
|
-
--decisions "Why built this way" \\
|
|
216000
|
-
--gotchas "Known traps" \\
|
|
216001
|
-
--ownership "@team"
|
|
216002
|
-
\`\`\`
|
|
216003
|
-
|
|
216004
|
-
Field reference:
|
|
216005
|
-
- \`--what\`, \`--how\`, \`--decisions\`, \`--gotchas\`, \`--ownership\`
|
|
216006
|
-
- \`--pattern\` (glob) or \`--where\` (explicit path) \u2014 mutually exclusive
|
|
216007
|
-
- \`--file <path>\` (on \`add\`) \u2014 anchor an existing markdown doc
|
|
216008
|
-
|
|
216009
|
-
## Output format
|
|
216010
|
-
|
|
216011
|
-
All CLI commands output JSON by default for agent consumption. Use \`--human\` for readable text.
|
|
216012
|
-
|
|
216013
|
-
## What Driftless is
|
|
216014
|
-
|
|
216015
|
-
Living context infrastructure for coding agents. CLI is the daily interface; the dashboard
|
|
216016
|
-
is observability for humans, not where agents work.
|
|
216017
|
-
|
|
216018
|
-
## What Driftless is not
|
|
216019
|
-
|
|
216020
|
-
Does not modify code. Does not approve or reject PRs. Does not block merges. Is not CI.
|
|
216021
|
-
Is not an AI reviewer.
|
|
216022
|
-
|
|
216023
|
-
Context delivery + structural verification only.
|
|
216024
|
-
`;
|
|
216025
|
-
function installSkillCommand() {
|
|
216026
|
-
const cwd = process.cwd();
|
|
216027
|
-
const agentsPath = (0, import_node_path6.resolve)(cwd, "AGENTS.md");
|
|
216028
|
-
if ((0, import_node_fs6.existsSync)(agentsPath)) {
|
|
216029
|
-
const existing = (0, import_node_fs6.readFileSync)(agentsPath, "utf8");
|
|
216030
|
-
if (existing.includes("# Driftless")) {
|
|
216031
|
-
console.log("Driftless section already present in AGENTS.md.");
|
|
216032
|
-
return;
|
|
216033
|
-
}
|
|
216034
|
-
(0, import_node_fs6.writeFileSync)(agentsPath, existing + "\n---\n\n" + template);
|
|
216035
|
-
console.log(`Appended Driftless section to ${agentsPath}`);
|
|
216036
|
-
} else {
|
|
216037
|
-
(0, import_node_fs6.writeFileSync)(agentsPath, template);
|
|
216038
|
-
console.log(`Driftless skill installed at ${agentsPath}`);
|
|
216039
|
-
}
|
|
216040
|
-
}
|
|
216041
|
-
|
|
216042
216184
|
// src/commands/login.ts
|
|
216043
216185
|
var import_node_fs7 = require("node:fs");
|
|
216044
216186
|
var import_node_path7 = require("node:path");
|
|
@@ -216256,7 +216398,7 @@ function pad2(s, n) {
|
|
|
216256
216398
|
}
|
|
216257
216399
|
|
|
216258
216400
|
// src/index.ts
|
|
216259
|
-
var VERSION = "0.1.
|
|
216401
|
+
var VERSION = "0.1.26";
|
|
216260
216402
|
var HELP_TEXT = `Driftless CLI v${VERSION} \u2014 Living repo context for humans and coding agents
|
|
216261
216403
|
|
|
216262
216404
|
Install: npm install -g @driftless-sh/cli
|
|
@@ -216476,7 +216618,7 @@ async function main() {
|
|
|
216476
216618
|
await sessionCommand(args.slice(1));
|
|
216477
216619
|
break;
|
|
216478
216620
|
case "install-skill":
|
|
216479
|
-
installSkillCommand();
|
|
216621
|
+
await installSkillCommand();
|
|
216480
216622
|
break;
|
|
216481
216623
|
case "login":
|
|
216482
216624
|
loginCommand(args.slice(1));
|