@locusai/cli 0.17.4 → 0.17.6
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/bin/locus.js +260 -11
- package/package.json +1 -1
package/bin/locus.js
CHANGED
|
@@ -1271,7 +1271,12 @@ function updateIssueLabels(number, addLabels, removeLabels, options = {}) {
|
|
|
1271
1271
|
gh(args, options);
|
|
1272
1272
|
}
|
|
1273
1273
|
function addIssueComment(number, body, options = {}) {
|
|
1274
|
-
|
|
1274
|
+
const cwd = options.cwd ?? process.cwd();
|
|
1275
|
+
execFileSync("gh", ["issue", "comment", String(number), "--body", body], {
|
|
1276
|
+
cwd,
|
|
1277
|
+
encoding: "utf-8",
|
|
1278
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
1279
|
+
});
|
|
1275
1280
|
}
|
|
1276
1281
|
function createMilestone(owner, repo, title, dueOn, description, options = {}) {
|
|
1277
1282
|
let args = `api repos/${owner}/${repo}/milestones -f title=${JSON.stringify(title)}`;
|
|
@@ -8532,6 +8537,7 @@ import {
|
|
|
8532
8537
|
writeFileSync as writeFileSync8
|
|
8533
8538
|
} from "node:fs";
|
|
8534
8539
|
import { join as join16 } from "node:path";
|
|
8540
|
+
import { createInterface as createInterface2 } from "node:readline";
|
|
8535
8541
|
function printHelp4() {
|
|
8536
8542
|
process.stderr.write(`
|
|
8537
8543
|
${bold("locus discuss")} — AI-powered architectural discussions
|
|
@@ -8564,7 +8570,7 @@ function generateId() {
|
|
|
8564
8570
|
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
8565
8571
|
}
|
|
8566
8572
|
async function discussCommand(projectRoot, args, flags = {}) {
|
|
8567
|
-
if (args[0] === "help"
|
|
8573
|
+
if (args[0] === "help") {
|
|
8568
8574
|
printHelp4();
|
|
8569
8575
|
return;
|
|
8570
8576
|
}
|
|
@@ -8578,6 +8584,14 @@ async function discussCommand(projectRoot, args, flags = {}) {
|
|
|
8578
8584
|
if (subcommand === "delete") {
|
|
8579
8585
|
return deleteDiscussion(projectRoot, args[1]);
|
|
8580
8586
|
}
|
|
8587
|
+
if (args.length === 0) {
|
|
8588
|
+
const topic2 = await promptForTopic();
|
|
8589
|
+
if (!topic2) {
|
|
8590
|
+
printHelp4();
|
|
8591
|
+
return;
|
|
8592
|
+
}
|
|
8593
|
+
return startDiscussion(projectRoot, topic2, flags);
|
|
8594
|
+
}
|
|
8581
8595
|
const topic = args.join(" ").trim();
|
|
8582
8596
|
return startDiscussion(projectRoot, topic, flags);
|
|
8583
8597
|
}
|
|
@@ -8657,6 +8671,21 @@ function deleteDiscussion(projectRoot, id) {
|
|
|
8657
8671
|
process.stderr.write(`${green("✓")} Deleted discussion: ${match.replace(".md", "")}
|
|
8658
8672
|
`);
|
|
8659
8673
|
}
|
|
8674
|
+
async function promptForTopic() {
|
|
8675
|
+
return new Promise((resolve2) => {
|
|
8676
|
+
const rl = createInterface2({
|
|
8677
|
+
input: process.stdin,
|
|
8678
|
+
output: process.stderr,
|
|
8679
|
+
terminal: true
|
|
8680
|
+
});
|
|
8681
|
+
process.stderr.write(`${bold("Discussion topic:")} `);
|
|
8682
|
+
rl.once("line", (line) => {
|
|
8683
|
+
rl.close();
|
|
8684
|
+
resolve2(line.trim());
|
|
8685
|
+
});
|
|
8686
|
+
rl.once("close", () => resolve2(""));
|
|
8687
|
+
});
|
|
8688
|
+
}
|
|
8660
8689
|
async function startDiscussion(projectRoot, topic, flags) {
|
|
8661
8690
|
const config = loadConfig(projectRoot);
|
|
8662
8691
|
const timer = createTimer();
|
|
@@ -8746,23 +8775,236 @@ var init_discuss = __esm(() => {
|
|
|
8746
8775
|
init_terminal();
|
|
8747
8776
|
});
|
|
8748
8777
|
|
|
8778
|
+
// src/commands/artifacts.ts
|
|
8779
|
+
var exports_artifacts = {};
|
|
8780
|
+
__export(exports_artifacts, {
|
|
8781
|
+
readArtifact: () => readArtifact,
|
|
8782
|
+
listArtifacts: () => listArtifacts,
|
|
8783
|
+
formatSize: () => formatSize,
|
|
8784
|
+
formatDate: () => formatDate2,
|
|
8785
|
+
artifactsCommand: () => artifactsCommand
|
|
8786
|
+
});
|
|
8787
|
+
import { existsSync as existsSync16, readdirSync as readdirSync8, readFileSync as readFileSync13, statSync as statSync4 } from "node:fs";
|
|
8788
|
+
import { join as join17 } from "node:path";
|
|
8789
|
+
function printHelp5() {
|
|
8790
|
+
process.stderr.write(`
|
|
8791
|
+
${bold("locus artifacts")} — View and manage AI-generated artifacts
|
|
8792
|
+
|
|
8793
|
+
${bold("Usage:")}
|
|
8794
|
+
locus artifacts ${dim("# List all artifacts")}
|
|
8795
|
+
locus artifacts show <name> ${dim("# Show artifact content")}
|
|
8796
|
+
locus artifacts plan <name> ${dim("# Convert artifact to a plan")}
|
|
8797
|
+
|
|
8798
|
+
${bold("Examples:")}
|
|
8799
|
+
locus artifacts
|
|
8800
|
+
locus artifacts show reduce-cli-terminal-output
|
|
8801
|
+
locus artifacts plan aws-instance-orchestration-prd
|
|
8802
|
+
|
|
8803
|
+
${dim("Artifact names support partial matching.")}
|
|
8804
|
+
|
|
8805
|
+
`);
|
|
8806
|
+
}
|
|
8807
|
+
function getArtifactsDir(projectRoot) {
|
|
8808
|
+
return join17(projectRoot, ".locus", "artifacts");
|
|
8809
|
+
}
|
|
8810
|
+
function listArtifacts(projectRoot) {
|
|
8811
|
+
const dir = getArtifactsDir(projectRoot);
|
|
8812
|
+
if (!existsSync16(dir))
|
|
8813
|
+
return [];
|
|
8814
|
+
return readdirSync8(dir).filter((f) => f.endsWith(".md")).map((fileName) => {
|
|
8815
|
+
const filePath = join17(dir, fileName);
|
|
8816
|
+
const stat = statSync4(filePath);
|
|
8817
|
+
return {
|
|
8818
|
+
name: fileName.replace(/\.md$/, ""),
|
|
8819
|
+
fileName,
|
|
8820
|
+
createdAt: stat.birthtime,
|
|
8821
|
+
size: stat.size
|
|
8822
|
+
};
|
|
8823
|
+
}).sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
|
|
8824
|
+
}
|
|
8825
|
+
function readArtifact(projectRoot, name) {
|
|
8826
|
+
const dir = getArtifactsDir(projectRoot);
|
|
8827
|
+
const fileName = name.endsWith(".md") ? name : `${name}.md`;
|
|
8828
|
+
const filePath = join17(dir, fileName);
|
|
8829
|
+
if (!existsSync16(filePath))
|
|
8830
|
+
return null;
|
|
8831
|
+
const stat = statSync4(filePath);
|
|
8832
|
+
return {
|
|
8833
|
+
content: readFileSync13(filePath, "utf-8"),
|
|
8834
|
+
info: {
|
|
8835
|
+
name: fileName.replace(/\.md$/, ""),
|
|
8836
|
+
fileName,
|
|
8837
|
+
createdAt: stat.birthtime,
|
|
8838
|
+
size: stat.size
|
|
8839
|
+
}
|
|
8840
|
+
};
|
|
8841
|
+
}
|
|
8842
|
+
function formatSize(bytes) {
|
|
8843
|
+
if (bytes < 1024)
|
|
8844
|
+
return `${bytes}B`;
|
|
8845
|
+
const kb = bytes / 1024;
|
|
8846
|
+
if (kb < 1024)
|
|
8847
|
+
return `${kb.toFixed(1)}KB`;
|
|
8848
|
+
return `${(kb / 1024).toFixed(1)}MB`;
|
|
8849
|
+
}
|
|
8850
|
+
function formatDate2(date) {
|
|
8851
|
+
return date.toLocaleDateString("en-US", {
|
|
8852
|
+
year: "numeric",
|
|
8853
|
+
month: "short",
|
|
8854
|
+
day: "numeric",
|
|
8855
|
+
hour: "2-digit",
|
|
8856
|
+
minute: "2-digit"
|
|
8857
|
+
});
|
|
8858
|
+
}
|
|
8859
|
+
async function artifactsCommand(projectRoot, args) {
|
|
8860
|
+
if (args[0] === "help") {
|
|
8861
|
+
printHelp5();
|
|
8862
|
+
return;
|
|
8863
|
+
}
|
|
8864
|
+
const subcommand = args[0];
|
|
8865
|
+
switch (subcommand) {
|
|
8866
|
+
case "show":
|
|
8867
|
+
case "view": {
|
|
8868
|
+
const name = args.slice(1).join(" ").trim();
|
|
8869
|
+
if (!name) {
|
|
8870
|
+
process.stderr.write(`${red("✗")} Please provide an artifact name.
|
|
8871
|
+
`);
|
|
8872
|
+
process.stderr.write(` Usage: ${bold("locus artifacts show <name>")}
|
|
8873
|
+
`);
|
|
8874
|
+
return;
|
|
8875
|
+
}
|
|
8876
|
+
showArtifact(projectRoot, name);
|
|
8877
|
+
break;
|
|
8878
|
+
}
|
|
8879
|
+
case "plan": {
|
|
8880
|
+
const name = args.slice(1).join(" ").trim();
|
|
8881
|
+
if (!name) {
|
|
8882
|
+
process.stderr.write(`${red("✗")} Please provide an artifact name.
|
|
8883
|
+
`);
|
|
8884
|
+
process.stderr.write(` Usage: ${bold("locus artifacts plan <name>")}
|
|
8885
|
+
`);
|
|
8886
|
+
return;
|
|
8887
|
+
}
|
|
8888
|
+
await convertToPlan(projectRoot, name);
|
|
8889
|
+
break;
|
|
8890
|
+
}
|
|
8891
|
+
default:
|
|
8892
|
+
listArtifactsCommand(projectRoot);
|
|
8893
|
+
break;
|
|
8894
|
+
}
|
|
8895
|
+
}
|
|
8896
|
+
function listArtifactsCommand(projectRoot) {
|
|
8897
|
+
const artifacts = listArtifacts(projectRoot);
|
|
8898
|
+
if (artifacts.length === 0) {
|
|
8899
|
+
process.stderr.write(`${dim("No artifacts found.")}
|
|
8900
|
+
`);
|
|
8901
|
+
return;
|
|
8902
|
+
}
|
|
8903
|
+
process.stderr.write(`
|
|
8904
|
+
${bold("Artifacts")} ${dim(`(${artifacts.length} total)`)}
|
|
8905
|
+
|
|
8906
|
+
`);
|
|
8907
|
+
for (let i = 0;i < artifacts.length; i++) {
|
|
8908
|
+
const a = artifacts[i];
|
|
8909
|
+
const idx = dim(`${String(i + 1).padStart(2)}.`);
|
|
8910
|
+
process.stderr.write(` ${idx} ${cyan(a.name)}
|
|
8911
|
+
`);
|
|
8912
|
+
process.stderr.write(` ${dim(`${formatDate2(a.createdAt)} • ${formatSize(a.size)}`)}
|
|
8913
|
+
`);
|
|
8914
|
+
}
|
|
8915
|
+
process.stderr.write(`
|
|
8916
|
+
${dim("Use")} ${bold("locus artifacts show <name>")} ${dim("to view content")}
|
|
8917
|
+
`);
|
|
8918
|
+
process.stderr.write(` ${dim("Use")} ${bold("locus artifacts plan <name>")} ${dim("to convert to a plan")}
|
|
8919
|
+
|
|
8920
|
+
`);
|
|
8921
|
+
}
|
|
8922
|
+
function showArtifact(projectRoot, name) {
|
|
8923
|
+
const result = readArtifact(projectRoot, name);
|
|
8924
|
+
if (!result) {
|
|
8925
|
+
const artifacts = listArtifacts(projectRoot);
|
|
8926
|
+
const matches = artifacts.filter((a) => a.name.toLowerCase().includes(name.toLowerCase()));
|
|
8927
|
+
if (matches.length === 1) {
|
|
8928
|
+
const match = readArtifact(projectRoot, matches[0].name);
|
|
8929
|
+
if (match) {
|
|
8930
|
+
printArtifact(match.info, match.content);
|
|
8931
|
+
return;
|
|
8932
|
+
}
|
|
8933
|
+
}
|
|
8934
|
+
if (matches.length > 1) {
|
|
8935
|
+
process.stderr.write(`${red("✗")} Multiple artifacts match "${name}":
|
|
8936
|
+
`);
|
|
8937
|
+
for (const m of matches) {
|
|
8938
|
+
process.stderr.write(` ${cyan(m.name)}
|
|
8939
|
+
`);
|
|
8940
|
+
}
|
|
8941
|
+
return;
|
|
8942
|
+
}
|
|
8943
|
+
process.stderr.write(`${red("✗")} Artifact "${name}" not found.
|
|
8944
|
+
`);
|
|
8945
|
+
process.stderr.write(` Run ${bold("locus artifacts")} to see available artifacts.
|
|
8946
|
+
`);
|
|
8947
|
+
return;
|
|
8948
|
+
}
|
|
8949
|
+
printArtifact(result.info, result.content);
|
|
8950
|
+
}
|
|
8951
|
+
function printArtifact(info, content) {
|
|
8952
|
+
const line = dim("─".repeat(50));
|
|
8953
|
+
process.stderr.write(`
|
|
8954
|
+
${bold(info.name)}
|
|
8955
|
+
${dim(`${formatDate2(info.createdAt)} • ${formatSize(info.size)}`)}
|
|
8956
|
+
${line}
|
|
8957
|
+
|
|
8958
|
+
`);
|
|
8959
|
+
process.stdout.write(`${content}
|
|
8960
|
+
`);
|
|
8961
|
+
}
|
|
8962
|
+
async function convertToPlan(projectRoot, name) {
|
|
8963
|
+
const result = readArtifact(projectRoot, name);
|
|
8964
|
+
if (!result) {
|
|
8965
|
+
const artifacts = listArtifacts(projectRoot);
|
|
8966
|
+
const matches = artifacts.filter((a) => a.name.toLowerCase().includes(name.toLowerCase()));
|
|
8967
|
+
if (matches.length === 1) {
|
|
8968
|
+
await runPlanConversion(projectRoot, matches[0].name);
|
|
8969
|
+
return;
|
|
8970
|
+
}
|
|
8971
|
+
process.stderr.write(`${red("✗")} Artifact "${name}" not found.
|
|
8972
|
+
`);
|
|
8973
|
+
process.stderr.write(` Run ${bold("locus artifacts")} to see available artifacts.
|
|
8974
|
+
`);
|
|
8975
|
+
return;
|
|
8976
|
+
}
|
|
8977
|
+
await runPlanConversion(projectRoot, result.info.name);
|
|
8978
|
+
}
|
|
8979
|
+
async function runPlanConversion(projectRoot, artifactName) {
|
|
8980
|
+
const { execCommand: execCommand2 } = await Promise.resolve().then(() => (init_exec(), exports_exec));
|
|
8981
|
+
process.stderr.write(`
|
|
8982
|
+
${bold("Converting artifact to plan:")} ${cyan(artifactName)}
|
|
8983
|
+
|
|
8984
|
+
`);
|
|
8985
|
+
await execCommand2(projectRoot, [`Create a plan according to ${artifactName}`], {});
|
|
8986
|
+
}
|
|
8987
|
+
var init_artifacts = __esm(() => {
|
|
8988
|
+
init_terminal();
|
|
8989
|
+
});
|
|
8990
|
+
|
|
8749
8991
|
// src/cli.ts
|
|
8750
8992
|
init_config();
|
|
8751
8993
|
init_context();
|
|
8752
8994
|
init_logger();
|
|
8753
8995
|
init_rate_limiter();
|
|
8754
8996
|
init_terminal();
|
|
8755
|
-
import { existsSync as
|
|
8756
|
-
import { join as
|
|
8997
|
+
import { existsSync as existsSync17, readFileSync as readFileSync14 } from "node:fs";
|
|
8998
|
+
import { join as join18 } from "node:path";
|
|
8757
8999
|
import { fileURLToPath } from "node:url";
|
|
8758
9000
|
function getCliVersion() {
|
|
8759
9001
|
const fallbackVersion = "0.0.0";
|
|
8760
|
-
const packageJsonPath =
|
|
8761
|
-
if (!
|
|
9002
|
+
const packageJsonPath = join18(fileURLToPath(new URL(".", import.meta.url)), "..", "package.json");
|
|
9003
|
+
if (!existsSync17(packageJsonPath)) {
|
|
8762
9004
|
return fallbackVersion;
|
|
8763
9005
|
}
|
|
8764
9006
|
try {
|
|
8765
|
-
const parsed = JSON.parse(
|
|
9007
|
+
const parsed = JSON.parse(readFileSync14(packageJsonPath, "utf-8"));
|
|
8766
9008
|
return parsed.version ?? fallbackVersion;
|
|
8767
9009
|
} catch {
|
|
8768
9010
|
return fallbackVersion;
|
|
@@ -8849,7 +9091,7 @@ function parseArgs(argv) {
|
|
|
8849
9091
|
const args = positional.slice(1);
|
|
8850
9092
|
return { command, args, flags };
|
|
8851
9093
|
}
|
|
8852
|
-
function
|
|
9094
|
+
function printHelp6() {
|
|
8853
9095
|
process.stderr.write(`
|
|
8854
9096
|
${bold("Locus")} ${dim(`v${VERSION}`)} — GitHub-native AI engineering assistant
|
|
8855
9097
|
|
|
@@ -8866,6 +9108,7 @@ ${bold("Commands:")}
|
|
|
8866
9108
|
${cyan("review")} AI-powered code review on PRs
|
|
8867
9109
|
${cyan("iterate")} Re-execute tasks with PR feedback
|
|
8868
9110
|
${cyan("discuss")} AI-powered architectural discussions
|
|
9111
|
+
${cyan("artifacts")} View and manage AI-generated artifacts
|
|
8869
9112
|
${cyan("status")} Dashboard view of current state
|
|
8870
9113
|
${cyan("config")} View and manage settings
|
|
8871
9114
|
${cyan("logs")} View, tail, and manage execution logs
|
|
@@ -8902,7 +9145,7 @@ async function main() {
|
|
|
8902
9145
|
process.exit(0);
|
|
8903
9146
|
}
|
|
8904
9147
|
if (parsed.flags.help && !parsed.command) {
|
|
8905
|
-
|
|
9148
|
+
printHelp6();
|
|
8906
9149
|
process.exit(0);
|
|
8907
9150
|
}
|
|
8908
9151
|
const command = resolveAlias(parsed.command);
|
|
@@ -8913,7 +9156,7 @@ async function main() {
|
|
|
8913
9156
|
try {
|
|
8914
9157
|
const root = getGitRoot(cwd);
|
|
8915
9158
|
if (isInitialized(root)) {
|
|
8916
|
-
logDir =
|
|
9159
|
+
logDir = join18(root, ".locus", "logs");
|
|
8917
9160
|
getRateLimiter(root);
|
|
8918
9161
|
}
|
|
8919
9162
|
} catch {}
|
|
@@ -8934,7 +9177,7 @@ async function main() {
|
|
|
8934
9177
|
printVersionNotice = startVersionCheck2(VERSION);
|
|
8935
9178
|
}
|
|
8936
9179
|
if (!command) {
|
|
8937
|
-
|
|
9180
|
+
printHelp6();
|
|
8938
9181
|
process.exit(0);
|
|
8939
9182
|
}
|
|
8940
9183
|
if (command === "init") {
|
|
@@ -9047,6 +9290,12 @@ async function main() {
|
|
|
9047
9290
|
});
|
|
9048
9291
|
break;
|
|
9049
9292
|
}
|
|
9293
|
+
case "artifacts": {
|
|
9294
|
+
const { artifactsCommand: artifactsCommand2 } = await Promise.resolve().then(() => (init_artifacts(), exports_artifacts));
|
|
9295
|
+
const artifactsArgs = parsed.flags.help ? ["help"] : parsed.args;
|
|
9296
|
+
await artifactsCommand2(projectRoot, artifactsArgs);
|
|
9297
|
+
break;
|
|
9298
|
+
}
|
|
9050
9299
|
case "upgrade": {
|
|
9051
9300
|
const { upgradeCommand: upgradeCommand2 } = await Promise.resolve().then(() => (init_upgrade(), exports_upgrade));
|
|
9052
9301
|
await upgradeCommand2(projectRoot, parsed.args, {
|