@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.
Files changed (2) hide show
  1. package/bin/locus.js +260 -11
  2. 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
- gh(`issue comment ${number} --body ${JSON.stringify(body)}`, options);
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" || args.length === 0) {
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 existsSync16, readFileSync as readFileSync13 } from "node:fs";
8756
- import { join as join17 } from "node:path";
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 = join17(fileURLToPath(new URL(".", import.meta.url)), "..", "package.json");
8761
- if (!existsSync16(packageJsonPath)) {
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(readFileSync13(packageJsonPath, "utf-8"));
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 printHelp5() {
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
- printHelp5();
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 = join17(root, ".locus", "logs");
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
- printHelp5();
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, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@locusai/cli",
3
- "version": "0.17.4",
3
+ "version": "0.17.6",
4
4
  "description": "GitHub-native AI engineering assistant",
5
5
  "type": "module",
6
6
  "bin": {