@driftless-sh/cli 0.1.25 → 0.1.28
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 +10 -10
- package/dist/index.js +306 -316
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -445,7 +445,7 @@ var require_util = __commonJS({
|
|
|
445
445
|
return path;
|
|
446
446
|
}
|
|
447
447
|
exports2.normalize = normalize;
|
|
448
|
-
function
|
|
448
|
+
function join2(aRoot, aPath) {
|
|
449
449
|
if (aRoot === "") {
|
|
450
450
|
aRoot = ".";
|
|
451
451
|
}
|
|
@@ -477,7 +477,7 @@ var require_util = __commonJS({
|
|
|
477
477
|
}
|
|
478
478
|
return joined;
|
|
479
479
|
}
|
|
480
|
-
exports2.join =
|
|
480
|
+
exports2.join = join2;
|
|
481
481
|
exports2.isAbsolute = function(aPath) {
|
|
482
482
|
return aPath.charAt(0) === "/" || urlRegexp.test(aPath);
|
|
483
483
|
};
|
|
@@ -650,7 +650,7 @@ var require_util = __commonJS({
|
|
|
650
650
|
parsed.path = parsed.path.substring(0, index + 1);
|
|
651
651
|
}
|
|
652
652
|
}
|
|
653
|
-
sourceURL =
|
|
653
|
+
sourceURL = join2(urlGenerate(parsed), sourceURL);
|
|
654
654
|
}
|
|
655
655
|
return normalize(sourceURL);
|
|
656
656
|
}
|
|
@@ -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 load --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.27";
|
|
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 = [];
|
|
@@ -215250,7 +215376,7 @@ async function contextCommand(args) {
|
|
|
215250
215376
|
}
|
|
215251
215377
|
process.exit(0);
|
|
215252
215378
|
}
|
|
215253
|
-
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)));
|
|
215254
215380
|
if (missingFiles.length > 0 && !isJSON) {
|
|
215255
215381
|
console.error(`File(s) not found locally: ${missingFiles.join(", ")}. Matching by pattern only.`);
|
|
215256
215382
|
}
|
|
@@ -215329,7 +215455,7 @@ async function contextCommand(args) {
|
|
|
215329
215455
|
let fileContent;
|
|
215330
215456
|
if (flags["file"]) {
|
|
215331
215457
|
try {
|
|
215332
|
-
fileContent = (0,
|
|
215458
|
+
fileContent = (0, import_node_fs5.readFileSync)(flags["file"], "utf-8");
|
|
215333
215459
|
} catch {
|
|
215334
215460
|
console.error(`File not found: ${flags["file"]}`);
|
|
215335
215461
|
process.exit(1);
|
|
@@ -215476,11 +215602,11 @@ async function contextCommand(args) {
|
|
|
215476
215602
|
}
|
|
215477
215603
|
return;
|
|
215478
215604
|
}
|
|
215479
|
-
if (subCommand === "
|
|
215605
|
+
if (subCommand === "sync") {
|
|
215480
215606
|
const slug = positional[1];
|
|
215481
215607
|
if (!slug) {
|
|
215482
|
-
console.error('Usage: driftless context
|
|
215483
|
-
console.error(' driftless context
|
|
215608
|
+
console.error('Usage: driftless context sync <topic> --doc <path> --files "<glob>"');
|
|
215609
|
+
console.error(' driftless context sync <topic> --note "..." --files "<glob>"');
|
|
215484
215610
|
process.exit(1);
|
|
215485
215611
|
}
|
|
215486
215612
|
const docFlag = flags["doc"];
|
|
@@ -215494,7 +215620,7 @@ async function contextCommand(args) {
|
|
|
215494
215620
|
let fileContent;
|
|
215495
215621
|
if (docFlag) {
|
|
215496
215622
|
try {
|
|
215497
|
-
fileContent = (0,
|
|
215623
|
+
fileContent = (0, import_node_fs5.readFileSync)(docFlag, "utf-8");
|
|
215498
215624
|
} catch {
|
|
215499
215625
|
console.error(`File not found: ${docFlag}`);
|
|
215500
215626
|
process.exit(1);
|
|
@@ -215506,7 +215632,7 @@ async function contextCommand(args) {
|
|
|
215506
215632
|
if (noteFlag) updates.decisions = noteFlag;
|
|
215507
215633
|
if (filesFlag) updates.where_files = filesFlag.split(",").map((f) => f.trim()).filter(Boolean);
|
|
215508
215634
|
if (patternFlag) updates.pattern = patternFlag;
|
|
215509
|
-
updates._event_detail = `
|
|
215635
|
+
updates._event_detail = `SYNC_ADDED: doc=${!!fileContent}, note=${!!noteFlag}, files=${filesFlag || "none"}`;
|
|
215510
215636
|
if (flags["dry-run"]) {
|
|
215511
215637
|
console.log(`Would anchor topic '${slug}':`);
|
|
215512
215638
|
if (docFlag) console.log(` doc: ${docFlag} (${fileContent ? fileContent.length : 0} chars)`);
|
|
@@ -215517,7 +215643,7 @@ async function contextCommand(args) {
|
|
|
215517
215643
|
console.log("\n(Dry run \u2014 no changes written to Driftless Cloud)");
|
|
215518
215644
|
}
|
|
215519
215645
|
if (isJSON) {
|
|
215520
|
-
emitJSON2({ dry_run: true, action: "
|
|
215646
|
+
emitJSON2({ dry_run: true, action: "sync", topic: slug, doc: docFlag, note: noteFlag, files: filesFlag, pattern: patternFlag });
|
|
215521
215647
|
}
|
|
215522
215648
|
process.exit(0);
|
|
215523
215649
|
}
|
|
@@ -215533,22 +215659,22 @@ async function contextCommand(args) {
|
|
|
215533
215659
|
}));
|
|
215534
215660
|
emitJSON2(sanitized);
|
|
215535
215661
|
} else {
|
|
215536
|
-
console.log(`Topic '${slug}'
|
|
215662
|
+
console.log(`Topic '${slug}' synced.`);
|
|
215537
215663
|
if (docFlag) console.log(` doc: ${docFlag} (${fileContent ? fileContent.length : 0} chars)`);
|
|
215538
215664
|
if (noteFlag) console.log(` note: ${noteFlag}`);
|
|
215539
215665
|
if (filesFlag) console.log(` files: ${filesFlag}`);
|
|
215540
215666
|
if (patternFlag) console.log(` pattern: ${patternFlag}`);
|
|
215541
215667
|
}
|
|
215542
215668
|
} catch (e) {
|
|
215543
|
-
console.error(`
|
|
215669
|
+
console.error(`Sync failed: ${formatError(e)}`);
|
|
215544
215670
|
process.exit(1);
|
|
215545
215671
|
}
|
|
215546
215672
|
return;
|
|
215547
215673
|
}
|
|
215548
|
-
if (subCommand === "
|
|
215674
|
+
if (subCommand === "load") {
|
|
215549
215675
|
const filesFlag = flags["files"];
|
|
215550
215676
|
if (!filesFlag) {
|
|
215551
|
-
console.error('Usage: driftless context
|
|
215677
|
+
console.error('Usage: driftless context load --files "path1,path2,path3"');
|
|
215552
215678
|
process.exit(1);
|
|
215553
215679
|
}
|
|
215554
215680
|
const files = filesFlag.split(",").map((f) => f.trim()).filter(Boolean);
|
|
@@ -215556,7 +215682,7 @@ async function contextCommand(args) {
|
|
|
215556
215682
|
console.error("No files provided.");
|
|
215557
215683
|
process.exit(1);
|
|
215558
215684
|
}
|
|
215559
|
-
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)));
|
|
215560
215686
|
if (missingFiles.length > 0 && !isJSON) {
|
|
215561
215687
|
console.error(`File(s) not found locally: ${missingFiles.join(", ")}. Matching by pattern only.`);
|
|
215562
215688
|
}
|
|
@@ -215601,28 +215727,35 @@ Warnings:`);
|
|
|
215601
215727
|
emitJSON2(results);
|
|
215602
215728
|
} else {
|
|
215603
215729
|
if (results.length > 0) {
|
|
215604
|
-
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
|
+
`);
|
|
215605
215732
|
for (const r of results) {
|
|
215606
|
-
|
|
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
|
+
}
|
|
215607
215742
|
}
|
|
215608
|
-
console.log(`
|
|
215609
|
-
Context delivered for ${files.length} file${files.length === 1 ? "" : "s"}.`);
|
|
215610
215743
|
} else {
|
|
215611
|
-
console.log(`No context topics match these files. No
|
|
215744
|
+
console.log(`No context topics match these files. No context covers the changed paths.`);
|
|
215612
215745
|
}
|
|
215613
215746
|
}
|
|
215614
215747
|
} catch (e) {
|
|
215615
|
-
console.error(`
|
|
215748
|
+
console.error(`Load failed: ${formatError(e)}`);
|
|
215616
215749
|
process.exit(1);
|
|
215617
215750
|
}
|
|
215618
215751
|
return;
|
|
215619
215752
|
}
|
|
215620
215753
|
if (subCommand === "export") {
|
|
215621
215754
|
const dir = flags["dir"] ?? ".driftless/watchers";
|
|
215622
|
-
const absDir = (0,
|
|
215755
|
+
const absDir = (0, import_node_path5.resolve)(process.cwd(), dir);
|
|
215623
215756
|
try {
|
|
215624
215757
|
const summaries = await api.get(`/workspaces/${workspaceSlug}/watchers`);
|
|
215625
|
-
(0,
|
|
215758
|
+
(0, import_node_fs5.mkdirSync)(absDir, { recursive: true });
|
|
215626
215759
|
for (const summary of summaries) {
|
|
215627
215760
|
const slug = summary.topic;
|
|
215628
215761
|
let ctx;
|
|
@@ -215652,10 +215785,10 @@ Context delivered for ${files.length} file${files.length === 1 ? "" : "s"}.`);
|
|
|
215652
215785
|
}
|
|
215653
215786
|
if (ctx.description.ownership) lines.push(`ownership: ${safeStr(ctx.description.ownership)}`);
|
|
215654
215787
|
const yamlContent = lines.join("\n") + "\n";
|
|
215655
|
-
(0,
|
|
215788
|
+
(0, import_node_fs5.writeFileSync)((0, import_node_path5.join)(absDir, `${slug}.yaml`), yamlContent, "utf-8");
|
|
215656
215789
|
}
|
|
215657
215790
|
if (!isJSON) {
|
|
215658
|
-
console.log(`Exported ${summaries.length}
|
|
215791
|
+
console.log(`Exported ${summaries.length} context topic${summaries.length === 1 ? "" : "s"} \u2192 ${dir}/`);
|
|
215659
215792
|
} else {
|
|
215660
215793
|
emitJSON2({ exported: summaries.length, dir });
|
|
215661
215794
|
}
|
|
@@ -215667,14 +215800,14 @@ Context delivered for ${files.length} file${files.length === 1 ? "" : "s"}.`);
|
|
|
215667
215800
|
}
|
|
215668
215801
|
if (subCommand === "import") {
|
|
215669
215802
|
const dir = flags["dir"] ?? ".driftless/watchers";
|
|
215670
|
-
const absDir = (0,
|
|
215671
|
-
if (!(0,
|
|
215803
|
+
const absDir = (0, import_node_path5.resolve)(process.cwd(), dir);
|
|
215804
|
+
if (!(0, import_node_fs5.existsSync)(absDir)) {
|
|
215672
215805
|
console.error(`Directory not found: ${dir}`);
|
|
215673
215806
|
process.exit(1);
|
|
215674
215807
|
}
|
|
215675
215808
|
let files;
|
|
215676
215809
|
try {
|
|
215677
|
-
files = (0,
|
|
215810
|
+
files = (0, import_node_fs5.readdirSync)(absDir).filter((f) => f.endsWith(".yaml"));
|
|
215678
215811
|
} catch (e) {
|
|
215679
215812
|
console.error(`Failed to read directory: ${formatError(e)}`);
|
|
215680
215813
|
process.exit(1);
|
|
@@ -215686,10 +215819,10 @@ Context delivered for ${files.length} file${files.length === 1 ? "" : "s"}.`);
|
|
|
215686
215819
|
let created = 0;
|
|
215687
215820
|
let updated = 0;
|
|
215688
215821
|
for (const file of files) {
|
|
215689
|
-
const filePath = (0,
|
|
215822
|
+
const filePath = (0, import_node_path5.join)(absDir, file);
|
|
215690
215823
|
let raw;
|
|
215691
215824
|
try {
|
|
215692
|
-
raw = (0,
|
|
215825
|
+
raw = (0, import_node_fs5.readFileSync)(filePath, "utf-8");
|
|
215693
215826
|
} catch {
|
|
215694
215827
|
console.error(` skipped ${file} (read error)`);
|
|
215695
215828
|
continue;
|
|
@@ -215768,15 +215901,15 @@ Done: ${created} created, ${updated} updated.`);
|
|
|
215768
215901
|
}
|
|
215769
215902
|
return;
|
|
215770
215903
|
}
|
|
215771
|
-
console.log(`Usage: driftless context <list|get|add|update|delete|search|
|
|
215904
|
+
console.log(`Usage: driftless context <list|get|add|update|delete|search|load> [args]
|
|
215772
215905
|
|
|
215773
215906
|
Run 'driftless help context' for full reference.`);
|
|
215774
215907
|
}
|
|
215775
215908
|
|
|
215776
215909
|
// src/commands/session.ts
|
|
215777
215910
|
init_git();
|
|
215778
|
-
var
|
|
215779
|
-
var
|
|
215911
|
+
var import_node_fs6 = require("node:fs");
|
|
215912
|
+
var import_node_path6 = require("node:path");
|
|
215780
215913
|
function parseArgs2(args) {
|
|
215781
215914
|
const flags = {};
|
|
215782
215915
|
const positional = [];
|
|
@@ -215838,7 +215971,7 @@ async function sessionCommand(args) {
|
|
|
215838
215971
|
}
|
|
215839
215972
|
process.exit(0);
|
|
215840
215973
|
}
|
|
215841
|
-
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)));
|
|
215842
215975
|
if (missingFiles.length > 0 && !isJSON) {
|
|
215843
215976
|
console.error(`Warning: ${missingFiles.length} file(s) not found locally \u2014 matching by pattern only.`);
|
|
215844
215977
|
}
|
|
@@ -215887,7 +216020,7 @@ async function sessionCommand(args) {
|
|
|
215887
216020
|
console.log(`Session started for ${files.length} file(s):
|
|
215888
216021
|
`);
|
|
215889
216022
|
for (const f of files) {
|
|
215890
|
-
const exists = (0,
|
|
216023
|
+
const exists = (0, import_node_fs6.existsSync)((0, import_node_path6.resolve)(process.cwd(), f));
|
|
215891
216024
|
console.log(` ${exists ? "\u2713" : "\u2717"} ${f}`);
|
|
215892
216025
|
}
|
|
215893
216026
|
if (missingFiles.length > 0) {
|
|
@@ -215929,7 +216062,7 @@ Rules that will be evaluated:`);
|
|
|
215929
216062
|
}
|
|
215930
216063
|
console.log("\nBefore finishing:");
|
|
215931
216064
|
console.log(" driftless scan --diff");
|
|
215932
|
-
console.log(" driftless session
|
|
216065
|
+
console.log(" driftless session end");
|
|
215933
216066
|
}
|
|
215934
216067
|
} catch (e) {
|
|
215935
216068
|
console.error(`Session start failed: ${formatError(e)}`);
|
|
@@ -215937,7 +216070,7 @@ Rules that will be evaluated:`);
|
|
|
215937
216070
|
}
|
|
215938
216071
|
return;
|
|
215939
216072
|
}
|
|
215940
|
-
if (subCommand === "
|
|
216073
|
+
if (subCommand === "end") {
|
|
215941
216074
|
const files = getChangedFilesList();
|
|
215942
216075
|
if (files.length === 0 && !isJSON) {
|
|
215943
216076
|
console.log("No local changes detected. Session clean.");
|
|
@@ -215956,9 +216089,9 @@ Rules that will be evaluated:`);
|
|
|
215956
216089
|
console.error(`Repo '${remote.repo}' not found. Run 'driftless init' first.`);
|
|
215957
216090
|
process.exit(1);
|
|
215958
216091
|
}
|
|
215959
|
-
const { getUncommittedDiff:
|
|
216092
|
+
const { getUncommittedDiff: getUncommittedDiff2, getStagedDiff: getStagedDiff2, getLastCommitHash: getLastCommitHash2, getAuthorName: getAuthorName2 } = await Promise.resolve().then(() => (init_git(), git_exports));
|
|
215960
216093
|
const staged = getStagedDiff2();
|
|
215961
|
-
const unstaged =
|
|
216094
|
+
const unstaged = getUncommittedDiff2();
|
|
215962
216095
|
const diff = [staged, unstaged].filter(Boolean).join("\n");
|
|
215963
216096
|
if (!diff && !isJSON) {
|
|
215964
216097
|
console.log("No changes to scan. Session clean.");
|
|
@@ -215987,7 +216120,7 @@ Rules that will be evaluated:`);
|
|
|
215987
216120
|
context_matched: contextResults.map((r) => r.context.topic)
|
|
215988
216121
|
});
|
|
215989
216122
|
} else {
|
|
215990
|
-
console.log(`Session
|
|
216123
|
+
console.log(`Session end \u2014 ${files.length} file(s) changed:
|
|
215991
216124
|
`);
|
|
215992
216125
|
for (const f of files) {
|
|
215993
216126
|
console.log(` \u270E ${f}`);
|
|
@@ -216028,167 +216161,24 @@ Context touched:`);
|
|
|
216028
216161
|
process.exit(1);
|
|
216029
216162
|
}
|
|
216030
216163
|
} catch (e) {
|
|
216031
|
-
console.error(`Session
|
|
216164
|
+
console.error(`Session end failed: ${formatError(e)}`);
|
|
216032
216165
|
process.exit(1);
|
|
216033
216166
|
}
|
|
216034
216167
|
return;
|
|
216035
216168
|
}
|
|
216036
|
-
console.log(`Usage: driftless session <start|
|
|
216169
|
+
console.log(`Usage: driftless session <start|end> [args]
|
|
216037
216170
|
|
|
216038
|
-
start
|
|
216171
|
+
start \u2014 Show relevant context before editing
|
|
216039
216172
|
--files "path1,path2" Files to check (default: local changes)
|
|
216040
216173
|
--json Output as JSON
|
|
216041
216174
|
|
|
216042
|
-
|
|
216175
|
+
end \u2014 Scan changes, check violations, suggest context updates
|
|
216043
216176
|
--json Output as JSON
|
|
216044
216177
|
|
|
216045
216178
|
Examples:
|
|
216046
216179
|
driftless session start --files "src/auth/**"
|
|
216047
216180
|
driftless session start # uses local changes
|
|
216048
|
-
driftless session
|
|
216049
|
-
}
|
|
216050
|
-
|
|
216051
|
-
// src/commands/install-skill.ts
|
|
216052
|
-
var import_node_fs6 = require("node:fs");
|
|
216053
|
-
var import_node_path6 = require("node:path");
|
|
216054
|
-
var template = `# Driftless \u2014 Live Repo Context
|
|
216055
|
-
|
|
216056
|
-
Driftless Cloud holds the team's living codebase context (topics, anchors, rules, gotchas).
|
|
216057
|
-
This file teaches you how to use it. Treat it as part of the toolchain, not documentation.
|
|
216058
|
-
|
|
216059
|
-
## Agent loop
|
|
216060
|
-
|
|
216061
|
-
### 1. Before editing \u2014 pull context
|
|
216062
|
-
|
|
216063
|
-
If you know the topic:
|
|
216064
|
-
|
|
216065
|
-
\`\`\`bash
|
|
216066
|
-
driftless context get <topic>
|
|
216067
|
-
\`\`\`
|
|
216068
|
-
|
|
216069
|
-
If you don't know which topic applies, discover it:
|
|
216070
|
-
|
|
216071
|
-
\`\`\`bash
|
|
216072
|
-
driftless context search "<keyword>"
|
|
216073
|
-
driftless context list
|
|
216074
|
-
\`\`\`
|
|
216075
|
-
|
|
216076
|
-
The \`get\` response is a canonical JSON object:
|
|
216077
|
-
|
|
216078
|
-
\`\`\`json
|
|
216079
|
-
{
|
|
216080
|
-
"topic": "<slug>",
|
|
216081
|
-
"summary": "...",
|
|
216082
|
-
"description": {
|
|
216083
|
-
"what": "...",
|
|
216084
|
-
"how": "...",
|
|
216085
|
-
"decisions": "...",
|
|
216086
|
-
"gotchas": [],
|
|
216087
|
-
"ownership": "..."
|
|
216088
|
-
},
|
|
216089
|
-
"anchors": {
|
|
216090
|
-
"files": [],
|
|
216091
|
-
"patterns": [],
|
|
216092
|
-
"repos": [],
|
|
216093
|
-
"docs": []
|
|
216094
|
-
},
|
|
216095
|
-
"components": [],
|
|
216096
|
-
"rules": [],
|
|
216097
|
-
"invariants": [],
|
|
216098
|
-
"violations": [],
|
|
216099
|
-
"events": [],
|
|
216100
|
-
"required_checks": [],
|
|
216101
|
-
"stale": { "is_stale": false, "reason": null },
|
|
216102
|
-
"metadata": { "created_by": "...", "last_updated": "...", "source": "manual|auto|doc|agent" }
|
|
216103
|
-
}
|
|
216104
|
-
\`\`\`
|
|
216105
|
-
|
|
216106
|
-
Always read:
|
|
216107
|
-
- \`description.what\` and \`description.how\` \u2014 what you're about to touch
|
|
216108
|
-
- \`description.decisions\` \u2014 why it's built this way
|
|
216109
|
-
- \`description.gotchas\` \u2014 known traps
|
|
216110
|
-
- \`anchors.docs\` \u2014 anchored team docs
|
|
216111
|
-
- \`stale.is_stale\` \u2014 if true, the context may be outdated; read \`stale.reason\`
|
|
216112
|
-
|
|
216113
|
-
Note: \`rules\`, \`invariants\`, \`violations\`, \`events\`, and \`required_checks\` may be empty
|
|
216114
|
-
during PR 1; they get populated in subsequent releases. Other fields are authoritative.
|
|
216115
|
-
|
|
216116
|
-
### 2. During work
|
|
216117
|
-
|
|
216118
|
-
Respect what the topic tells you. Follow decisions and gotchas. If anchored docs contradict
|
|
216119
|
-
code, the docs are usually intent \u2014 verify before changing.
|
|
216120
|
-
|
|
216121
|
-
### 3. Before finishing \u2014 scan
|
|
216122
|
-
|
|
216123
|
-
\`\`\`bash
|
|
216124
|
-
driftless scan --diff
|
|
216125
|
-
\`\`\`
|
|
216126
|
-
|
|
216127
|
-
This evaluates your uncommitted changes against active Cloud rules. Exit code:
|
|
216128
|
-
- \`0\` \u2014 clean
|
|
216129
|
-
- \`1\` \u2014 violations (read each \`explanation\`, fix, re-scan until clean)
|
|
216130
|
-
|
|
216131
|
-
Do not push if scan returns violations.
|
|
216132
|
-
|
|
216133
|
-
### 4. After learning \u2014 write context back
|
|
216134
|
-
|
|
216135
|
-
If you discovered something durable about the codebase, write it back so the next session benefits:
|
|
216136
|
-
|
|
216137
|
-
Update an existing topic:
|
|
216138
|
-
|
|
216139
|
-
\`\`\`bash
|
|
216140
|
-
driftless context update <topic> \\
|
|
216141
|
-
--gotchas "Setting X to Y breaks Z under condition W"
|
|
216142
|
-
\`\`\`
|
|
216143
|
-
|
|
216144
|
-
Or add a new topic:
|
|
216145
|
-
|
|
216146
|
-
\`\`\`bash
|
|
216147
|
-
driftless context add "<name>" \\
|
|
216148
|
-
--what "What this piece is" \\
|
|
216149
|
-
--how "How it works" \\
|
|
216150
|
-
--pattern "src/path/**" \\
|
|
216151
|
-
--decisions "Why built this way" \\
|
|
216152
|
-
--gotchas "Known traps" \\
|
|
216153
|
-
--ownership "@team"
|
|
216154
|
-
\`\`\`
|
|
216155
|
-
|
|
216156
|
-
Field reference:
|
|
216157
|
-
- \`--what\`, \`--how\`, \`--decisions\`, \`--gotchas\`, \`--ownership\`
|
|
216158
|
-
- \`--pattern\` (glob) or \`--where\` (explicit path) \u2014 mutually exclusive
|
|
216159
|
-
- \`--file <path>\` (on \`add\`) \u2014 anchor an existing markdown doc
|
|
216160
|
-
|
|
216161
|
-
## Output format
|
|
216162
|
-
|
|
216163
|
-
All CLI commands output JSON by default for agent consumption. Use \`--human\` for readable text.
|
|
216164
|
-
|
|
216165
|
-
## What Driftless is
|
|
216166
|
-
|
|
216167
|
-
Living context infrastructure for coding agents. CLI is the daily interface; the dashboard
|
|
216168
|
-
is observability for humans, not where agents work.
|
|
216169
|
-
|
|
216170
|
-
## What Driftless is not
|
|
216171
|
-
|
|
216172
|
-
Does not modify code. Does not approve or reject PRs. Does not block merges. Is not CI.
|
|
216173
|
-
Is not an AI reviewer.
|
|
216174
|
-
|
|
216175
|
-
Context delivery + structural verification only.
|
|
216176
|
-
`;
|
|
216177
|
-
function installSkillCommand() {
|
|
216178
|
-
const cwd = process.cwd();
|
|
216179
|
-
const agentsPath = (0, import_node_path6.resolve)(cwd, "AGENTS.md");
|
|
216180
|
-
if ((0, import_node_fs6.existsSync)(agentsPath)) {
|
|
216181
|
-
const existing = (0, import_node_fs6.readFileSync)(agentsPath, "utf8");
|
|
216182
|
-
if (existing.includes("# Driftless")) {
|
|
216183
|
-
console.log("Driftless section already present in AGENTS.md.");
|
|
216184
|
-
return;
|
|
216185
|
-
}
|
|
216186
|
-
(0, import_node_fs6.writeFileSync)(agentsPath, existing + "\n---\n\n" + template);
|
|
216187
|
-
console.log(`Appended Driftless section to ${agentsPath}`);
|
|
216188
|
-
} else {
|
|
216189
|
-
(0, import_node_fs6.writeFileSync)(agentsPath, template);
|
|
216190
|
-
console.log(`Driftless skill installed at ${agentsPath}`);
|
|
216191
|
-
}
|
|
216181
|
+
driftless session end # scan + context report`);
|
|
216192
216182
|
}
|
|
216193
216183
|
|
|
216194
216184
|
// src/commands/login.ts
|
|
@@ -216408,7 +216398,7 @@ function pad2(s, n) {
|
|
|
216408
216398
|
}
|
|
216409
216399
|
|
|
216410
216400
|
// src/index.ts
|
|
216411
|
-
var VERSION = "0.1.
|
|
216401
|
+
var VERSION = "0.1.27";
|
|
216412
216402
|
var HELP_TEXT = `Driftless CLI v${VERSION} \u2014 Living repo context for humans and coding agents
|
|
216413
216403
|
|
|
216414
216404
|
Install: npm install -g @driftless-sh/cli
|
|
@@ -216417,7 +216407,7 @@ API: https://api.driftless.icu/api/v1
|
|
|
216417
216407
|
|
|
216418
216408
|
Agent loop:
|
|
216419
216409
|
driftless session start Show context before editing
|
|
216420
|
-
driftless session
|
|
216410
|
+
driftless session end Scan changes + context report
|
|
216421
216411
|
driftless context list List all context topics
|
|
216422
216412
|
driftless context get <topic> Live view of one topic before editing
|
|
216423
216413
|
driftless context get --diff What context matters for my local changes?
|
|
@@ -216434,7 +216424,7 @@ Commands:
|
|
|
216434
216424
|
init Smart init: scan \u2192 detect patterns \u2192 create topics + rules + anchor docs
|
|
216435
216425
|
scan Evaluate staged + uncommitted changes against rules
|
|
216436
216426
|
scan --diff Evaluate uncommitted changes only
|
|
216437
|
-
session Agent session: start (context) \u2192
|
|
216427
|
+
session Agent session: start (context) \u2192 end (scan + report)
|
|
216438
216428
|
context Live repo context (topics, search, anchors)
|
|
216439
216429
|
install-skill Install AGENTS.md into current repo
|
|
216440
216430
|
doctor Check environment health (auth, API, git, workspace, repo, baseline)
|
|
@@ -216447,18 +216437,18 @@ Context subcommands:
|
|
|
216447
216437
|
get --diff Match topics for local uncommitted changes
|
|
216448
216438
|
search "<query>" Full-text search across topics
|
|
216449
216439
|
add "<name>" --what "..." Create a new topic
|
|
216450
|
-
|
|
216451
|
-
|
|
216440
|
+
sync <topic> --doc <path> Sync a doc to a topic
|
|
216441
|
+
sync <topic> --note "..." Add a note to a topic
|
|
216452
216442
|
update <topic> --what "..." Update topic fields
|
|
216453
216443
|
update <topic> --gotcha "..." Append a gotcha
|
|
216454
216444
|
update <topic> --invariant ".." Append an invariant
|
|
216455
216445
|
update <topic> --check "..." Append a required check
|
|
216456
216446
|
delete <topic> Delete a topic
|
|
216457
|
-
|
|
216447
|
+
load --files "p1,p2" Match topics by file paths
|
|
216458
216448
|
|
|
216459
216449
|
Session subcommands:
|
|
216460
216450
|
start [--files "p1,p2"] Show relevant context before editing
|
|
216461
|
-
|
|
216451
|
+
end Scan changes, check violations, suggest updates
|
|
216462
216452
|
|
|
216463
216453
|
Flags:
|
|
216464
216454
|
--json Output as JSON (default is human-readable)
|
|
@@ -216478,7 +216468,7 @@ Output format:
|
|
|
216478
216468
|
|
|
216479
216469
|
Examples:
|
|
216480
216470
|
driftless session start --files "src/auth/**"
|
|
216481
|
-
driftless session
|
|
216471
|
+
driftless session end
|
|
216482
216472
|
|
|
216483
216473
|
driftless context add "b2b-guard" \\
|
|
216484
216474
|
--what "Guard que protege endpoints B2B" \\
|
|
@@ -216488,7 +216478,7 @@ Examples:
|
|
|
216488
216478
|
driftless context get sdk
|
|
216489
216479
|
driftless context search "auth"
|
|
216490
216480
|
driftless scan --diff
|
|
216491
|
-
driftless context
|
|
216481
|
+
driftless context load --files "src/auth/auth.controller.ts" --dry-run
|
|
216492
216482
|
`;
|
|
216493
216483
|
function showCommandHelp(cmd) {
|
|
216494
216484
|
const help = {
|
|
@@ -216546,11 +216536,11 @@ Subcommands:
|
|
|
216546
216536
|
get --diff Match topics for local uncommitted changes
|
|
216547
216537
|
search "<query>" Full-text search across topics
|
|
216548
216538
|
add "<name>" [opts] Create a new topic
|
|
216549
|
-
|
|
216550
|
-
|
|
216539
|
+
sync <topic> --doc <path> Sync a doc to a topic
|
|
216540
|
+
sync <topic> --note "..." Add a note to a topic
|
|
216551
216541
|
update <topic> [opts] Update topic fields
|
|
216552
216542
|
delete <topic> Delete a topic
|
|
216553
|
-
|
|
216543
|
+
load --files "p1,p2,..." Match topics for given file paths
|
|
216554
216544
|
|
|
216555
216545
|
List filters:
|
|
216556
216546
|
--stale Only stale topics
|
|
@@ -216571,8 +216561,8 @@ Examples:
|
|
|
216571
216561
|
driftless context search "auth"
|
|
216572
216562
|
driftless context add "sdk" --pattern "src/sdk/**" --what "Public SDK"
|
|
216573
216563
|
driftless context update sdk --gotchas "Reset token on org switch"
|
|
216574
|
-
driftless context
|
|
216575
|
-
driftless context
|
|
216564
|
+
driftless context sync auth --doc docs/auth.md --files "src/auth/**"
|
|
216565
|
+
driftless context load --files "src/auth/guard.ts" --dry-run
|
|
216576
216566
|
`,
|
|
216577
216567
|
"install-skill": `driftless install-skill
|
|
216578
216568
|
|
|
@@ -216628,7 +216618,7 @@ async function main() {
|
|
|
216628
216618
|
await sessionCommand(args.slice(1));
|
|
216629
216619
|
break;
|
|
216630
216620
|
case "install-skill":
|
|
216631
|
-
installSkillCommand();
|
|
216621
|
+
await installSkillCommand();
|
|
216632
216622
|
break;
|
|
216633
216623
|
case "login":
|
|
216634
216624
|
loginCommand(args.slice(1));
|