product-discovery-cli 0.0.1 → 0.0.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "product-discovery-cli",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "CLI to consume the Product Discovery Agent API",
5
5
  "license": "Apache-2.0",
6
6
  "author": "AuronForge",
@@ -8,7 +8,8 @@ class RunDiscoveryFlow {
8
8
  this.presenter = presenter;
9
9
  }
10
10
 
11
- async execute({ apiUrl, saveDefaults }) {
11
+ async execute({ apiUrl, lang, saveDefaults, i18n }) {
12
+ this.i18n = i18n;
12
13
  this.presenter.printHeader();
13
14
 
14
15
  let continueOuter = true;
@@ -18,22 +19,23 @@ class RunDiscoveryFlow {
18
19
  let shouldImprove = true;
19
20
 
20
21
  while (shouldImprove) {
21
- this.presenter.info("What do you want to run discovery for?");
22
- const idea = await this.prompt.askInput("Describe your idea/problem/application/pain:", {
23
- required: true
22
+ this.presenter.info(this.i18n.t("askIdea"));
23
+ const idea = await this.prompt.askInput(this.i18n.t("describeIdea"), {
24
+ required: true,
25
+ requiredMessage: this.i18n.t("required")
24
26
  });
25
27
 
26
28
  const attemptText = session.addIdea(idea);
27
29
 
28
- const spinner = this.presenter.spinner("Calling the discovery API...");
30
+ const spinner = this.presenter.spinner(this.i18n.t("callingApi"));
29
31
  let result;
30
32
  try {
31
- result = await this.apiClient.runDiscovery(attemptText, apiUrl);
32
- spinner.succeed("Discovery completed.");
33
+ result = await this.apiClient.runDiscovery(attemptText, apiUrl, lang);
34
+ spinner.succeed(this.i18n.t("discoveryCompleted"));
33
35
  } catch (error) {
34
- spinner.fail("Discovery failed.");
36
+ spinner.fail(this.i18n.t("discoveryFailed"));
35
37
  this.presenter.error(error.message);
36
- const retry = await this.prompt.askYesNo("Do you want to try again?");
38
+ const retry = await this.prompt.askYesNo(this.i18n.t("askRetry"));
37
39
  if (!retry) {
38
40
  return;
39
41
  }
@@ -43,7 +45,7 @@ class RunDiscoveryFlow {
43
45
  this.presenter.json(result);
44
46
 
45
47
  if (saveDefaults.autoSave === false) {
46
- const improve = await this.prompt.askYesNo("Do you want to improve the result?");
48
+ const improve = await this.prompt.askYesNo(this.i18n.t("askImprove"));
47
49
  if (!improve) {
48
50
  shouldImprove = false;
49
51
  continueOuter = false;
@@ -54,12 +56,12 @@ class RunDiscoveryFlow {
54
56
 
55
57
  const saveInfo = await this.storage.saveJson(result, saveDefaults);
56
58
  if (saveInfo.saved) {
57
- this.presenter.success(`Saved to: ${saveInfo.fullPath}`);
59
+ this.presenter.success(`${this.i18n.t("savedTo")} ${saveInfo.fullPath}`);
58
60
  shouldImprove = false;
59
61
  continue;
60
62
  }
61
63
 
62
- const improve = await this.prompt.askYesNo("Do you want to improve the result?");
64
+ const improve = await this.prompt.askYesNo(this.i18n.t("askImprove"));
63
65
  if (!improve) {
64
66
  shouldImprove = false;
65
67
  continueOuter = false;
@@ -71,7 +73,7 @@ class RunDiscoveryFlow {
71
73
  break;
72
74
  }
73
75
 
74
- const another = await this.prompt.askYesNo("Do you want to run discovery for another idea?");
76
+ const another = await this.prompt.askYesNo(this.i18n.t("askAnother"));
75
77
  if (!another) {
76
78
  continueOuter = false;
77
79
  }
@@ -1,19 +1,26 @@
1
1
  const { Command } = require("commander");
2
+ const { getTranslator } = require("../infrastructure/i18n");
2
3
 
3
4
  function buildOptions(defaultApiUrl) {
4
5
  const program = new Command();
5
- program
6
+ program.parse(process.argv);
7
+ const preOpts = program.opts();
8
+ const i18n = getTranslator(preOpts.lang || "pt-br");
9
+
10
+ const programWithDesc = new Command();
11
+ programWithDesc
6
12
  .name("product-discovery")
7
- .description("Generate a structured product discovery using the Product Discovery Agent API")
8
- .option("-u, --api-url <url>", "API URL", defaultApiUrl)
9
- .option("-c, --config <path>", "Path to JSON config file")
10
- .option("-s, --save", "Auto-save the JSON result without prompting")
11
- .option("-o, --output <dir>", "Default output directory")
12
- .option("-f, --file <name>", "Default output filename")
13
- .option("--no-save", "Disable saving prompt");
13
+ .description(i18n.t("cliDescription"))
14
+ .option("-u, --api-url <url>", i18n.t("optApiUrl"), defaultApiUrl)
15
+ .option("-l, --lang <language>", i18n.t("optLang"), "pt-br")
16
+ .option("-c, --config <path>", i18n.t("optConfig"))
17
+ .option("-s, --save", i18n.t("optSave"))
18
+ .option("-o, --output <dir>", i18n.t("optOutput"))
19
+ .option("-f, --file <name>", i18n.t("optFile"))
20
+ .option("--no-save", i18n.t("optNoSave"));
14
21
 
15
- program.parse(process.argv);
16
- return program.opts();
22
+ programWithDesc.parse(process.argv);
23
+ return programWithDesc.opts();
17
24
  }
18
25
 
19
26
  module.exports = { buildOptions };
@@ -2,9 +2,15 @@ const { stdout: output } = require("node:process");
2
2
  const chalk = require("chalk");
3
3
  const boxen = require("boxen");
4
4
  const ora = require("ora");
5
+ const pkg = require("../../package.json");
5
6
 
6
7
  class ConsolePresenter {
8
+ constructor(i18n = null) {
9
+ this.i18n = i18n;
10
+ }
11
+
7
12
  printHeader() {
13
+ if (!this.i18n) return;
8
14
  const logo = chalk.cyan(
9
15
  String.raw`
10
16
  ____ _ _ ____ _
@@ -14,9 +20,12 @@ class ConsolePresenter {
14
20
  |_| |_| \___/ \__,_|\__,_|\___|\__| |____/|_|___/\___\___/ \_/ \___|_|
15
21
  `
16
22
  );
17
- const title = chalk.bold.cyan("Product Discovery CLI");
18
- const subtitle = chalk.gray("Generate a structured product discovery via the Product Discovery Agent API.");
19
- const banner = `${logo}\n${title}\n${subtitle}`;
23
+ const title = chalk.bold.cyan(this.i18n.t("headerTitle"));
24
+ const subtitle = chalk.gray(this.i18n.t("headerSubtitle"));
25
+ const author = chalk.gray(`${this.i18n.t("headerAuthor")}: ${pkg.author}`);
26
+ const version = chalk.gray(`${this.i18n.t("headerVersion")}: ${pkg.version}`);
27
+ const license = chalk.gray(`${this.i18n.t("headerLicense")}: ${pkg.license}`);
28
+ const banner = `${logo}\n${title}\n${subtitle}\n\n${author}\n${version}\n${license}`;
20
29
  output.write(
21
30
  boxen(banner, {
22
31
  padding: 1,
@@ -37,11 +46,11 @@ class ConsolePresenter {
37
46
  }
38
47
 
39
48
  error(message) {
40
- output.write(`${chalk.red("Error:")} ${message}\n\n`);
49
+ output.write(`${chalk.red(this.i18n.t("error"))} ${message}\n\n`);
41
50
  }
42
51
 
43
52
  json(payload) {
44
- output.write(`\n${chalk.bold("Generated discovery JSON:")}\n\n`);
53
+ output.write(`\n${chalk.bold(this.i18n.t("generatedDiscovery"))}\n\n`);
45
54
  output.write(`${chalk.gray(JSON.stringify(payload, null, 2))}\n\n`);
46
55
  }
47
56
 
@@ -1,9 +1,9 @@
1
1
  class ProductDiscoveryApi {
2
- async runDiscovery(problemText, apiUrl) {
2
+ async runDiscovery(problemText, apiUrl, lang = "pt-br") {
3
3
  const response = await fetch(apiUrl, {
4
4
  method: "POST",
5
5
  headers: { "Content-Type": "application/json" },
6
- body: JSON.stringify({ problem: problemText })
6
+ body: JSON.stringify({ problem: problemText, lang })
7
7
  });
8
8
 
9
9
  const text = await response.text();
@@ -13,14 +13,14 @@ class PromptService {
13
13
  return answer;
14
14
  }
15
15
 
16
- async askInput(message, { required = false } = {}) {
16
+ async askInput(message, { required = false, requiredMessage = "This field is required." } = {}) {
17
17
  const { answer } = await inquirer.prompt([
18
18
  {
19
19
  type: "input",
20
20
  name: "answer",
21
21
  message,
22
22
  validate: required
23
- ? (value) => (value && value.trim() ? true : "This field is required.")
23
+ ? (value) => (value && value.trim() ? true : requiredMessage)
24
24
  : () => true
25
25
  }
26
26
  ]);
@@ -0,0 +1,93 @@
1
+ const translations = {
2
+ "pt-br": {
3
+ // CLI Options
4
+ cliDescription: "Gerar uma descoberta de produto estruturada usando a API do Product Discovery Agent",
5
+ optApiUrl: "URL da API",
6
+ optLang: "Código do idioma (pt-br, en-us)",
7
+ optConfig: "Caminho para o arquivo de configuração JSON",
8
+ optSave: "Salvar automaticamente o resultado JSON sem perguntar",
9
+ optOutput: "Diretório de saída padrão",
10
+ optFile: "Nome do arquivo de saída padrão",
11
+ optNoSave: "Desabilitar prompt de salvamento",
12
+
13
+ // Header
14
+ headerTitle: "Product Discovery CLI",
15
+ headerSubtitle: "Gerar uma descoberta de produto estruturada via API do Product Discovery Agent.",
16
+ headerAuthor: "Autor",
17
+ headerVersion: "Versão",
18
+ headerLicense: "Licença",
19
+
20
+ // ConsolePresenter
21
+ generatedDiscovery: "Discovery JSON gerado:",
22
+ error: "Erro:",
23
+
24
+ // RunDiscoveryFlow
25
+ askIdea: "O que você quer descobrir?",
26
+ describeIdea: "Descreva sua ideia/problema/aplicação/dor:",
27
+ callingApi: "Chamando a API de discovery...",
28
+ discoveryCompleted: "Discovery completado.",
29
+ discoveryFailed: "Discovery falhou.",
30
+ askRetry: "Você quer tentar novamente?",
31
+ askImprove: "Você quer melhorar o resultado?",
32
+ savedTo: "Salvo em:",
33
+ askAnother: "Você quer rodar discovery para outra ideia?",
34
+
35
+ // CliController
36
+ configError: "Erro no config:",
37
+
38
+ // Validation
39
+ required: "Este campo é obrigatório"
40
+ },
41
+
42
+ "en-us": {
43
+ // CLI Options
44
+ cliDescription: "Generate a structured product discovery using the Product Discovery Agent API",
45
+ optApiUrl: "API URL",
46
+ optLang: "Language code (pt-br, en-us)",
47
+ optConfig: "Path to JSON config file",
48
+ optSave: "Auto-save the JSON result without prompting",
49
+ optOutput: "Default output directory",
50
+ optFile: "Default output filename",
51
+ optNoSave: "Disable saving prompt",
52
+
53
+ // Header
54
+ headerTitle: "Product Discovery CLI",
55
+ headerSubtitle: "Generate a structured product discovery via the Product Discovery Agent API.",
56
+ headerAuthor: "Author",
57
+ headerVersion: "Version",
58
+ headerLicense: "License",
59
+
60
+ // ConsolePresenter
61
+ generatedDiscovery: "Generated discovery JSON:",
62
+ error: "Error:",
63
+
64
+ // RunDiscoveryFlow
65
+ askIdea: "What do you want to run discovery for?",
66
+ describeIdea: "Describe your idea/problem/application/pain:",
67
+ callingApi: "Calling the discovery API...",
68
+ discoveryCompleted: "Discovery completed.",
69
+ discoveryFailed: "Discovery failed.",
70
+ askRetry: "Do you want to try again?",
71
+ askImprove: "Do you want to improve the result?",
72
+ savedTo: "Saved to:",
73
+ askAnother: "Do you want to run discovery for another idea?",
74
+
75
+ // CliController
76
+ configError: "Config error:",
77
+
78
+ // Validation
79
+ required: "This field is required"
80
+ }
81
+ };
82
+
83
+ function getTranslator(lang = "pt-br") {
84
+ const normalizedLang = lang.toLowerCase();
85
+ const texts = translations[normalizedLang] || translations["pt-br"];
86
+
87
+ return {
88
+ t: (key) => texts[key] || key,
89
+ lang: normalizedLang
90
+ };
91
+ }
92
+
93
+ module.exports = { getTranslator };
@@ -1,5 +1,6 @@
1
1
  const { buildOptions } = require("../config/cliOptions");
2
2
  const { ConfigLoader } = require("../infrastructure/ConfigLoader");
3
+ const { getTranslator } = require("../infrastructure/i18n");
3
4
 
4
5
  class CliController {
5
6
  constructor({ defaultApiUrl, prompt, presenter, apiClient, storage, useCase }) {
@@ -13,11 +14,17 @@ class CliController {
13
14
 
14
15
  async start() {
15
16
  const cliOptions = buildOptions(this.defaultApiUrl);
17
+ const lang = cliOptions.lang || "pt-br";
18
+ const i18n = getTranslator(lang);
19
+
20
+ // Update presenter with i18n
21
+ this.presenter.i18n = i18n;
22
+
16
23
  let config = {};
17
24
  try {
18
25
  config = new ConfigLoader().load(cliOptions.config);
19
26
  } catch (error) {
20
- this.presenter.error(`Config error: ${error.message}`);
27
+ this.presenter.error(`${i18n.t("configError")} ${error.message}`);
21
28
  return;
22
29
  }
23
30
 
@@ -31,7 +38,7 @@ class CliController {
31
38
  prompt: this.prompt
32
39
  };
33
40
 
34
- await this.useCase.execute({ apiUrl, saveDefaults });
41
+ await this.useCase.execute({ apiUrl, lang, saveDefaults, i18n });
35
42
  }
36
43
  }
37
44