@locusai/cli 0.17.4 → 0.17.5

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