product-discovery-cli 0.0.1 → 0.0.3
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 +236 -0
- package/package.json +1 -1
- package/src/application/RunDiscoveryFlow.js +15 -13
- package/src/config/cliOptions.js +17 -10
- package/src/infrastructure/ConsolePresenter.js +14 -5
- package/src/infrastructure/ProductDiscoveryApi.js +2 -2
- package/src/infrastructure/PromptService.js +2 -2
- package/src/infrastructure/i18n.js +93 -0
- package/src/presentation/CliController.js +9 -2
package/README.md
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
# Product Discovery CLI
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/product-discovery-cli)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
[](https://nodejs.org)
|
|
6
|
+
|
|
7
|
+
A professional command-line interface to interact with the Product Discovery Agent API. Generate structured product discoveries powered by AI using an intuitive and colorful CLI experience.
|
|
8
|
+
|
|
9
|
+
## ✨ Features
|
|
10
|
+
|
|
11
|
+
- 🌐 **Internationalization**: Support for Portuguese (pt-br) and English (en-us)
|
|
12
|
+
- 🎨 **Professional UX**: Colorful output, interactive prompts, and loading spinners
|
|
13
|
+
- 🏗️ **Clean Architecture**: Built with SOLID principles and layered architecture
|
|
14
|
+
- 💾 **Auto-save**: Optional automatic JSON result saving
|
|
15
|
+
- 🔄 **Iterative Improvement**: Refine discoveries with additional context
|
|
16
|
+
- ⚙️ **Configurable**: CLI flags, config files, and environment variables
|
|
17
|
+
- ✅ **Well-tested**: Comprehensive unit tests with 90%+ coverage
|
|
18
|
+
- 📦 **Easy to install**: Available as a global npm package
|
|
19
|
+
|
|
20
|
+
## 📦 Installation
|
|
21
|
+
|
|
22
|
+
### Global Installation (Recommended)
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install -g product-discovery-cli
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Local Installation
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install product-discovery-cli
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## 🚀 Quick Start
|
|
35
|
+
|
|
36
|
+
After global installation, simply run:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
product-discovery
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The CLI will guide you through an interactive flow to generate product discoveries.
|
|
43
|
+
|
|
44
|
+
## 📖 Usage
|
|
45
|
+
|
|
46
|
+
### Basic Usage
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# Run with default settings (Portuguese)
|
|
50
|
+
product-discovery
|
|
51
|
+
|
|
52
|
+
# Run in English
|
|
53
|
+
product-discovery --lang en-us
|
|
54
|
+
|
|
55
|
+
# Specify API URL
|
|
56
|
+
product-discovery --api-url http://localhost:3000/api/v1/discovery
|
|
57
|
+
|
|
58
|
+
# Auto-save results without prompting
|
|
59
|
+
product-discovery --save --output ./results
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### CLI Options
|
|
63
|
+
|
|
64
|
+
| Option | Alias | Description | Default |
|
|
65
|
+
|--------|-------|-------------|---------|
|
|
66
|
+
| `--api-url <url>` | `-u` | API endpoint URL | `http://localhost:3000/api/v1/discovery` |
|
|
67
|
+
| `--lang <code>` | `-l` | Language (pt-br, en-us) | `pt-br` |
|
|
68
|
+
| `--config <path>` | `-c` | Path to JSON config file | - |
|
|
69
|
+
| `--save` | `-s` | Auto-save without prompting | `false` |
|
|
70
|
+
| `--output <dir>` | `-o` | Default output directory | - |
|
|
71
|
+
| `--file <name>` | `-f` | Default output filename | - |
|
|
72
|
+
| `--no-save` | - | Disable saving prompt | - |
|
|
73
|
+
|
|
74
|
+
### Configuration File
|
|
75
|
+
|
|
76
|
+
Create a JSON configuration file to set default options:
|
|
77
|
+
|
|
78
|
+
```json
|
|
79
|
+
{
|
|
80
|
+
"apiUrl": "http://localhost:3000/api/v1/discovery",
|
|
81
|
+
"lang": "en-us",
|
|
82
|
+
"autoSave": true,
|
|
83
|
+
"defaultOutputDir": "./discoveries",
|
|
84
|
+
"defaultFileName": "discovery.json"
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Use it with:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
product-discovery --config ./config.json
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Environment Variables
|
|
95
|
+
|
|
96
|
+
Set environment variables for configuration:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
export PRODUCT_DISCOVERY_API_URL=http://localhost:3000/api/v1/discovery
|
|
100
|
+
product-discovery
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## 🌍 Internationalization
|
|
104
|
+
|
|
105
|
+
The CLI supports multiple languages:
|
|
106
|
+
|
|
107
|
+
- **Portuguese (pt-br)**: Default language
|
|
108
|
+
- **English (en-us)**: Full translation of all messages
|
|
109
|
+
|
|
110
|
+
Switch languages using the `--lang` flag:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
product-discovery --lang en-us
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## 🏗️ Architecture
|
|
117
|
+
|
|
118
|
+
This project follows **Clean Architecture** principles with clear separation of concerns:
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
src/
|
|
122
|
+
├── application/ # Use cases and business logic
|
|
123
|
+
├── domain/ # Domain entities and utilities
|
|
124
|
+
├── infrastructure/ # External dependencies (API, storage, i18n)
|
|
125
|
+
├── presentation/ # CLI controllers and routes
|
|
126
|
+
└── config/ # Configuration and CLI options
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Key Principles
|
|
130
|
+
|
|
131
|
+
- **SOLID**: Single Responsibility, Open/Closed, Dependency Inversion
|
|
132
|
+
- **Dependency Injection**: All dependencies injected via constructors
|
|
133
|
+
- **Testability**: Easy to mock and test each layer independently
|
|
134
|
+
|
|
135
|
+
## 🛠️ Development
|
|
136
|
+
|
|
137
|
+
### Prerequisites
|
|
138
|
+
|
|
139
|
+
- Node.js >= 20
|
|
140
|
+
|
|
141
|
+
### Setup
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
# Clone the repository
|
|
145
|
+
git clone https://github.com/AuronForge/product-discovery-cli.git
|
|
146
|
+
cd product-discovery-cli
|
|
147
|
+
|
|
148
|
+
# Install dependencies
|
|
149
|
+
npm install
|
|
150
|
+
|
|
151
|
+
# Run locally
|
|
152
|
+
npm start
|
|
153
|
+
|
|
154
|
+
# Run tests
|
|
155
|
+
npm test
|
|
156
|
+
|
|
157
|
+
# Run tests with coverage
|
|
158
|
+
npm run test:coverage
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Testing
|
|
162
|
+
|
|
163
|
+
The project includes comprehensive unit tests with Jest:
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
# Run all tests
|
|
167
|
+
npm test
|
|
168
|
+
|
|
169
|
+
# Run with coverage report
|
|
170
|
+
npm run test:coverage
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Coverage thresholds are set to 90% for all metrics.
|
|
174
|
+
|
|
175
|
+
## 📝 Interactive Flow
|
|
176
|
+
|
|
177
|
+
The CLI guides you through an intuitive workflow:
|
|
178
|
+
|
|
179
|
+
1. **Welcome**: Display branded banner with metadata
|
|
180
|
+
2. **Input**: Describe your idea/problem/application
|
|
181
|
+
3. **Processing**: Call the Product Discovery API
|
|
182
|
+
4. **Results**: View the generated JSON discovery
|
|
183
|
+
5. **Save**: Optionally save results to a file
|
|
184
|
+
6. **Improve**: Refine with additional context (optional)
|
|
185
|
+
7. **Repeat**: Run discovery for another idea (optional)
|
|
186
|
+
|
|
187
|
+
## 🎨 Output Example
|
|
188
|
+
|
|
189
|
+
```
|
|
190
|
+
╭────────────────────────────────────────────────────────────────────╮
|
|
191
|
+
│ │
|
|
192
|
+
│ ____ _ _ ____ _ │
|
|
193
|
+
│ | _ \ _ __ ___ __| |_ _ ___| |_ | _ \(_)___ ___ ___ │
|
|
194
|
+
│ | |_) | '__/ _ \ / _ | | | |/ __| __| | | | | / __|/ __/ _ \ │
|
|
195
|
+
│ | __/| | | (_) | (_| | |_| | (__| |_ | |_| | \__ \ (_| (_) | │
|
|
196
|
+
│ |_| |_| \___/ \__,_|\__,_|\___|\__| |____/|_|___/\___\___/ │
|
|
197
|
+
│ │
|
|
198
|
+
│ Product Discovery CLI │
|
|
199
|
+
│ Generate a structured product discovery via the API. │
|
|
200
|
+
│ │
|
|
201
|
+
│ Author: AuronForge │
|
|
202
|
+
│ Version: 0.0.2 │
|
|
203
|
+
│ License: Apache-2.0 │
|
|
204
|
+
│ │
|
|
205
|
+
╰────────────────────────────────────────────────────────────────────╯
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## 📄 License
|
|
209
|
+
|
|
210
|
+
This project is licensed under the Apache-2.0 License.
|
|
211
|
+
|
|
212
|
+
## 👤 Author
|
|
213
|
+
|
|
214
|
+
**José Eduardo Trindade E Marques**
|
|
215
|
+
|
|
216
|
+
- Company: AuronForge 🚀
|
|
217
|
+
- Email: edu.temarques@gmail.com
|
|
218
|
+
- LinkedIn: [linkedin.com/in/edu-marques29](https://linkedin.com/in/edu-marques29)
|
|
219
|
+
|
|
220
|
+
## 🤝 Contributing
|
|
221
|
+
|
|
222
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
223
|
+
|
|
224
|
+
## 🐛 Issues
|
|
225
|
+
|
|
226
|
+
Found a bug or have a suggestion? Please open an issue on [GitHub](https://github.com/AuronForge/product-discovery-cli/issues).
|
|
227
|
+
|
|
228
|
+
## 🔗 Links
|
|
229
|
+
|
|
230
|
+
- [npm Package](https://www.npmjs.com/package/product-discovery-cli)
|
|
231
|
+
- [GitHub Repository](https://github.com/AuronForge/product-discovery-cli)
|
|
232
|
+
- [Product Discovery Agent API](https://github.com/AuronForge/product-discovery-agent)
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
Made with ❤️ by AuronForge
|
package/package.json
CHANGED
|
@@ -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("
|
|
22
|
-
const idea = await this.prompt.askInput("
|
|
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("
|
|
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(
|
|
33
|
+
result = await this.apiClient.runDiscovery(attemptText, apiUrl, lang);
|
|
34
|
+
spinner.succeed(this.i18n.t("discoveryCompleted"));
|
|
33
35
|
} catch (error) {
|
|
34
|
-
spinner.fail(
|
|
36
|
+
spinner.fail(this.i18n.t("discoveryFailed"));
|
|
35
37
|
this.presenter.error(error.message);
|
|
36
|
-
const retry = await this.prompt.askYesNo("
|
|
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("
|
|
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(
|
|
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("
|
|
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("
|
|
76
|
+
const another = await this.prompt.askYesNo(this.i18n.t("askAnother"));
|
|
75
77
|
if (!another) {
|
|
76
78
|
continueOuter = false;
|
|
77
79
|
}
|
package/src/config/cliOptions.js
CHANGED
|
@@ -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("
|
|
8
|
-
.option("-u, --api-url <url>", "
|
|
9
|
-
.option("-
|
|
10
|
-
.option("-
|
|
11
|
-
.option("-
|
|
12
|
-
.option("-
|
|
13
|
-
.option("
|
|
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
|
-
|
|
16
|
-
return
|
|
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("
|
|
18
|
-
const subtitle = chalk.gray(
|
|
19
|
-
const
|
|
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("
|
|
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("
|
|
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 :
|
|
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(
|
|
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
|
|