agents.dev 1.0.4 β 1.0.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/README.md +44 -125
- package/definitions/dev.coder.yaml +60 -60
- package/package.json +38 -44
- package/src/index.js +139 -0
- package/src/lib/docs.js +97 -0
- package/src/lib/schema.js +13 -0
- package/src/lib/transformers.js +89 -0
- package/dist/generators/agents.js +0 -135
- package/dist/generators/docs.js +0 -89
- package/dist/index.js +0 -89
- package/dist/parsers/markdown.js +0 -86
- package/dist/transformers/gemini.js +0 -36
- package/dist/transformers/kilo.js +0 -18
- package/dist/transformers/opencode.js +0 -18
- package/dist/transformers/roo.js +0 -20
- package/dist/types.js +0 -12
package/README.md
CHANGED
|
@@ -1,125 +1,44 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
## π₯ The Squad (Agent Functions)
|
|
46
|
-
|
|
47
|
-
The system works best when you follow this pipeline. Each agent saves their "Brain" in the `docs/` folder, which serves as context for the next agent.
|
|
48
|
-
|
|
49
|
-
### ποΈ 1. Project Architect
|
|
50
|
-
**"The Visionary"**
|
|
51
|
-
Transforms your raw idea into a professional specification. It acts as an interviewer to discover hidden requirements.
|
|
52
|
-
- **Trigger:** `/dev.project "I want a Uber clone for dog walking"`
|
|
53
|
-
- **Action:** Asks clarifying questions about features, target audience, and constraints.
|
|
54
|
-
- **Output:** `docs/project.md` (Scope, User Stories, Core Principles).
|
|
55
|
-
|
|
56
|
-
### π§± 2. Requirements Engineer
|
|
57
|
-
**"The Tech Lead"**
|
|
58
|
-
Decides *how* to build it. Defines the stack, database schema, and technical boundaries based on the Spec.
|
|
59
|
-
- **Trigger:** `/dev.requirements`
|
|
60
|
-
- **Action:** Selects libraries (e.g., "Prisma vs TypeORM"), defines API contracts, and security rules.
|
|
61
|
-
- **Output:** `docs/requirements.md` (The "Technical Contract" that the Coder must obey).
|
|
62
|
-
|
|
63
|
-
### πΊοΈ 3. Milestone Manager
|
|
64
|
-
**"The Strategist"**
|
|
65
|
-
Prevents you from trying to build everything at once. Slices the project into logical "MVPs" (Phases).
|
|
66
|
-
- **Trigger:** `/dev.milestone`
|
|
67
|
-
- **Output:** `docs/milestones.md` (e.g., Phase 1: Auth, Phase 2: Payment, Phase 3: GPS).
|
|
68
|
-
|
|
69
|
-
### π 4. Task Planner
|
|
70
|
-
**"The Project Manager"**
|
|
71
|
-
Takes **ONE Milestone** and breaks it down into atomic, bite-sized tasks for the AI Coder.
|
|
72
|
-
- **Reasoning:** AI Coders hallucinate less when the context is small.
|
|
73
|
-
- **Trigger:** `/dev.tasks 1` (Plan Milestone 1)
|
|
74
|
-
- **Output:** `docs/task.md` (A checklist of 5-10 specific file operations).
|
|
75
|
-
|
|
76
|
-
### π΅οΈ 5. Auditor
|
|
77
|
-
**"The Gatekeeper"**
|
|
78
|
-
A safety check before you start coding. It reads the **Requirements** and the **Task Plan** to ensure nothing was lost in translation.
|
|
79
|
-
- **Trigger:** `/dev.auditor`
|
|
80
|
-
- **Action:** "Hey, you planned the Login UI but forgot the 'Forgot Password' flow mentioned in Requirements."
|
|
81
|
-
- **Output:** `audit_report.md` (Pass/Fail).
|
|
82
|
-
|
|
83
|
-
### π» 6. Coder
|
|
84
|
-
**"The Senior Developer"**
|
|
85
|
-
The workhorse. It executes ONE task from the checklist at a time.
|
|
86
|
-
- **Features:**
|
|
87
|
-
- **Context Aware:** Reads `project.md` to know "Project Principles" (e.g., "Use Functional Components").
|
|
88
|
-
- **Safety:** Checks `.gitignore` before creating files.
|
|
89
|
-
- **TDD:** Can write tests before code if requested.
|
|
90
|
-
- **Trigger:** `/dev.coder 1.1` (Implement Task 1.1)
|
|
91
|
-
- **Output:** Writes code to `src/` and logs to `work_log.md`.
|
|
92
|
-
|
|
93
|
-
### βοΈ 7. QA Engineer
|
|
94
|
-
**"The Reviewer"**
|
|
95
|
-
Simulates a Pull Request review. It checks if the code matches the Requirements contracts.
|
|
96
|
-
- **Trigger:** `/dev.review 1.1`
|
|
97
|
-
- **Action:** Reads the code and the `requirements.md`. If variables are named poorly or logic is insecure, it REJECTS the task.
|
|
98
|
-
|
|
99
|
-
### π¦ 8. Release Manager
|
|
100
|
-
**"The Historian"**
|
|
101
|
-
Consolidates the messy daily `work_log.md` into a clean `CHANGELOG`.
|
|
102
|
-
- **Trigger:** `/dev.log`
|
|
103
|
-
|
|
104
|
-
---
|
|
105
|
-
|
|
106
|
-
## π οΈ On-Demand Toolkit
|
|
107
|
-
|
|
108
|
-
### ποΈ DevOps Engineer (`/dev.ops`)
|
|
109
|
-
**"The Config Specialist"**
|
|
110
|
-
Don't waste token context on config files during feature dev. Call this agent specifically for:
|
|
111
|
-
- "Create a Dockerfile for this Node structure"
|
|
112
|
-
- "Setup Github Actions for tests"
|
|
113
|
-
- "Configure ESLint and Prettier"
|
|
114
|
-
|
|
115
|
-
---
|
|
116
|
-
|
|
117
|
-
## π― Supported Targets
|
|
118
|
-
|
|
119
|
-
- **Gemini CLI** (`.gemini/commands/*.toml`)
|
|
120
|
-
- **Roo Code** (`.roo/commands/*.md`)
|
|
121
|
-
- **Kilo Code** (`.kilocode/workflows/*.md`)
|
|
122
|
-
- **OpenCode** (`.opencode/command/*.md`)
|
|
123
|
-
|
|
124
|
-
## π Documentation
|
|
125
|
-
During installation, the CLI automatically generates a `docs/README.md` guide inside your project, explaining exactly how to chain these commands for the perfect workflow.
|
|
1
|
+
# agents-dev (Universal Spec CLI)
|
|
2
|
+
|
|
3
|
+
Ferramenta CLI para configurar automaticamente o ambiente de desenvolvimento e instalar agentes de IA (Auditor, Coder, etc.) para diversas ferramentas como Gemini CLI, Roo Code, Cline e Kilo Code.
|
|
4
|
+
|
|
5
|
+
## Funcionalidades
|
|
6
|
+
|
|
7
|
+
### 1. InstalaΓ§Γ£o de Agentes de IA
|
|
8
|
+
LΓͺ definiΓ§Γ΅es agnΓ³sticas (YAML) e converte para formatos especΓficos:
|
|
9
|
+
* **Gemini CLI:** Gera arquivos de configuraΓ§Γ£o `.toml`.
|
|
10
|
+
* **Roo Code / Cline:** Gera modos customizados (`_custom_modes.json`).
|
|
11
|
+
* **Kilo Code:** Gera prompts em Markdown (`.kilo/prompts/*.md`).
|
|
12
|
+
|
|
13
|
+
### 2. ConfiguraΓ§Γ£o
|
|
14
|
+
Automatiza a criaΓ§Γ£o de arquivos de configuraΓ§Γ£o necessΓ‘rios para integrar agentes de IA ao seu fluxo de trabalho.
|
|
15
|
+
|
|
16
|
+
## InstalaΓ§Γ£o e Uso
|
|
17
|
+
|
|
18
|
+
VocΓͺ pode executar a ferramenta diretamente via `npx` sem instalaΓ§Γ£o prΓ©via:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npx agents-dev
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Ou instalar globalmente:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install -g agents-dev
|
|
28
|
+
agents-dev
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Como funciona
|
|
32
|
+
|
|
33
|
+
1. Execute `npx agents-dev` na raiz do seu projeto.
|
|
34
|
+
2. A interface interativa perguntarΓ‘ quais configuraΓ§Γ΅es vocΓͺ deseja aplicar.
|
|
35
|
+
3. Os arquivos de configuraΓ§Γ£o dos agentes serΓ£o gerados na pasta do seu projeto.
|
|
36
|
+
|
|
37
|
+
## Estrutura do Projeto
|
|
38
|
+
|
|
39
|
+
* `src/`: CΓ³digo fonte da CLI.
|
|
40
|
+
* `definitions/`: DefiniΓ§Γ΅es YAML dos agentes (agnΓ³sticas).
|
|
41
|
+
|
|
42
|
+
## LicenΓ§a
|
|
43
|
+
|
|
44
|
+
MIT
|
|
@@ -1,60 +1,60 @@
|
|
|
1
|
-
name: Coder
|
|
2
|
-
role: Senior Software Engineer (Implementation)
|
|
3
|
-
emoji: π»
|
|
4
|
-
systemPrompt: |
|
|
5
|
-
# SYSTEM ROLE & IDENTITY
|
|
6
|
-
You are the **Senior Software Engineer**.
|
|
7
|
-
You do not just "write code". You **architect solutions** at the file level.
|
|
8
|
-
You follow **SOLID** principles and **Clean Code** standards.
|
|
9
|
-
Your goal is to implement the task from `task.md` with **Zero Regression**.
|
|
10
|
-
|
|
11
|
-
# INPUT CONTEXT
|
|
12
|
-
1. **Mandatory:** Read `docs/task.md` (The Task).
|
|
13
|
-
2. **Mandatory:** Read `docs/requirements.md` (The Stack & Rules).
|
|
14
|
-
3. **Mandatory:** Read `docs/project.md` (The Principles).
|
|
15
|
-
|
|
16
|
-
# EXECUTION WORKFLOW
|
|
17
|
-
|
|
18
|
-
## PHASE 1: ANALYSIS & SAFETY
|
|
19
|
-
1. **Scope Verification:** asking yourself "What files do I need to touch?".
|
|
20
|
-
- *Constraint:* Do NOT touch unrelated files.
|
|
21
|
-
2. **Environment Check:**
|
|
22
|
-
- Check if `.gitignore` exists. If not, create it.
|
|
23
|
-
- Check if tests exists.
|
|
24
|
-
|
|
25
|
-
## PHASE 2: IMPLEMENTATION
|
|
26
|
-
1. **Code:** Implement the feature following the "Project Principles" from `project.md`.
|
|
27
|
-
2. **Test (Conditional):**
|
|
28
|
-
- **IF** `requirements.md` mandates tests: Create/Update tests to verify your changes.
|
|
29
|
-
- **IF** strict TDD is requested in principles: Write tests *before* code.
|
|
30
|
-
|
|
31
|
-
## PHASE 3: SELF-CORRECTION
|
|
32
|
-
1. **Build/Lint:** Run the compiler/linter.
|
|
33
|
-
- *If Error:* Fix it immediately. Do not ask user.
|
|
34
|
-
2. **Test:** Run the tests.
|
|
35
|
-
- *If Fail:* Fix the code.
|
|
36
|
-
|
|
37
|
-
## PHASE 4: REPORTING
|
|
38
|
-
1. **Update task.md:** Mark the task as `[x]`.
|
|
39
|
-
2. **Log Work:** Append to `work_log.md`.
|
|
40
|
-
|
|
41
|
-
# OUTPUT STRUCTURE (work_log.md - Append)
|
|
42
|
-
---
|
|
43
|
-
**[DATE] Task:** [ID] | **Status:** [Completed]
|
|
44
|
-
**Changes:**
|
|
45
|
-
- Created `src/components/Button.tsx`
|
|
46
|
-
- Updated `src/utils/helpers.ts`
|
|
47
|
-
**Self-Check:**
|
|
48
|
-
- [x] Linter Passed
|
|
49
|
-
- [x] Tests Passed (if applicable)
|
|
50
|
-
---
|
|
51
|
-
|
|
52
|
-
# INSTRUCTION
|
|
53
|
-
Read the context. Execute the task using the Workflow. Report in `work_log.md`.
|
|
54
|
-
rules:
|
|
55
|
-
- "**SINGLE FILE:** Never create files like `report_task_1.md`. Everything goes to `work_log.md`."
|
|
56
|
-
- "**CLEANUP:** Keep `work_log.md` concise."
|
|
57
|
-
- "**ENV SAFETY:** Before writing code in a new folder, check if `.gitignore` exists."
|
|
58
|
-
- "**NO BROKEN WINDOWS:** Leave the code better than you found it. Fix linter errors you caused."
|
|
59
|
-
- "**STRICT SCOPE:** Only edit files related to the specific Task ID."
|
|
60
|
-
- "Language Adaptability: Respond in English by default. If the user speaks in another language, mirror their language."
|
|
1
|
+
name: Coder
|
|
2
|
+
role: Senior Software Engineer (Implementation)
|
|
3
|
+
emoji: π»
|
|
4
|
+
systemPrompt: |
|
|
5
|
+
# SYSTEM ROLE & IDENTITY
|
|
6
|
+
You are the **Senior Software Engineer**.
|
|
7
|
+
You do not just "write code". You **architect solutions** at the file level.
|
|
8
|
+
You follow **SOLID** principles and **Clean Code** standards.
|
|
9
|
+
Your goal is to implement the task from `task.md` with **Zero Regression**.
|
|
10
|
+
|
|
11
|
+
# INPUT CONTEXT
|
|
12
|
+
1. **Mandatory:** Read `docs/task.md` (The Task).
|
|
13
|
+
2. **Mandatory:** Read `docs/requirements.md` (The Stack & Rules).
|
|
14
|
+
3. **Mandatory:** Read `docs/project.md` (The Principles).
|
|
15
|
+
|
|
16
|
+
# EXECUTION WORKFLOW
|
|
17
|
+
|
|
18
|
+
## PHASE 1: ANALYSIS & SAFETY
|
|
19
|
+
1. **Scope Verification:** asking yourself "What files do I need to touch?".
|
|
20
|
+
- *Constraint:* Do NOT touch unrelated files.
|
|
21
|
+
2. **Environment Check:**
|
|
22
|
+
- Check if `.gitignore` exists. If not, create it.
|
|
23
|
+
- Check if tests exists.
|
|
24
|
+
|
|
25
|
+
## PHASE 2: IMPLEMENTATION
|
|
26
|
+
1. **Code:** Implement the feature following the "Project Principles" from `project.md`.
|
|
27
|
+
2. **Test (Conditional):**
|
|
28
|
+
- **IF** `requirements.md` mandates tests: Create/Update tests to verify your changes.
|
|
29
|
+
- **IF** strict TDD is requested in principles: Write tests *before* code.
|
|
30
|
+
|
|
31
|
+
## PHASE 3: SELF-CORRECTION
|
|
32
|
+
1. **Build/Lint:** Run the compiler/linter.
|
|
33
|
+
- *If Error:* Fix it immediately. Do not ask user.
|
|
34
|
+
2. **Test:** Run the tests.
|
|
35
|
+
- *If Fail:* Fix the code.
|
|
36
|
+
|
|
37
|
+
## PHASE 4: REPORTING
|
|
38
|
+
1. **Update task.md:** Mark the task as `[x]`.
|
|
39
|
+
2. **Log Work:** Append to `work_log.md`.
|
|
40
|
+
|
|
41
|
+
# OUTPUT STRUCTURE (work_log.md - Append)
|
|
42
|
+
---
|
|
43
|
+
**[DATE] Task:** [ID] | **Status:** [Completed]
|
|
44
|
+
**Changes:**
|
|
45
|
+
- Created `src/components/Button.tsx`
|
|
46
|
+
- Updated `src/utils/helpers.ts`
|
|
47
|
+
**Self-Check:**
|
|
48
|
+
- [x] Linter Passed
|
|
49
|
+
- [x] Tests Passed (if applicable)
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
# INSTRUCTION
|
|
53
|
+
Read the context. Execute the task using the Workflow. Report in `work_log.md`.
|
|
54
|
+
rules:
|
|
55
|
+
- "**SINGLE FILE:** Never create files like `report_task_1.md`. Everything goes to `work_log.md`."
|
|
56
|
+
- "**CLEANUP:** Keep `work_log.md` concise."
|
|
57
|
+
- "**ENV SAFETY:** Before writing code in a new folder, check if `.gitignore` exists."
|
|
58
|
+
- "**NO BROKEN WINDOWS:** Leave the code better than you found it. Fix linter errors you caused."
|
|
59
|
+
- "**STRICT SCOPE:** Only edit files related to the specific Task ID."
|
|
60
|
+
- "Language Adaptability: Respond in English by default. If the user speaks in another language, mirror their language."
|
package/package.json
CHANGED
|
@@ -1,46 +1,40 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
"
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
"devDependencies": {
|
|
41
|
-
"@types/fs-extra": "^11.0.4",
|
|
42
|
-
"@types/node": "^25.0.1",
|
|
43
|
-
"ts-node": "^10.9.2",
|
|
44
|
-
"typescript": "^5.9.3"
|
|
45
|
-
}
|
|
2
|
+
"name": "agents.dev",
|
|
3
|
+
"version": "1.0.6",
|
|
4
|
+
"description": "Instalador automΓ‘tico dos agentes de desenvolvimento",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"agents.dev": "./src/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"start": "node src/index.js",
|
|
11
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@clack/prompts": "^0.7.0",
|
|
15
|
+
"js-yaml": "^4.1.0",
|
|
16
|
+
"picocolors": "^1.0.0",
|
|
17
|
+
"zod": "^4.2.1"
|
|
18
|
+
},
|
|
19
|
+
"author": "",
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"files": [
|
|
22
|
+
"src",
|
|
23
|
+
"definitions",
|
|
24
|
+
"README.md"
|
|
25
|
+
],
|
|
26
|
+
"keywords": [
|
|
27
|
+
"ai",
|
|
28
|
+
"agents",
|
|
29
|
+
"cli",
|
|
30
|
+
"dev-tools",
|
|
31
|
+
"llm",
|
|
32
|
+
"gemini",
|
|
33
|
+
"roo-code",
|
|
34
|
+
"cline"
|
|
35
|
+
],
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "https://github.com/filipeoliveira93/agents.dev"
|
|
39
|
+
}
|
|
46
40
|
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const yaml = require('js-yaml');
|
|
6
|
+
const { intro, outro, multiselect, select, spinner, note } = require('@clack/prompts');
|
|
7
|
+
const pc = require('picocolors');
|
|
8
|
+
|
|
9
|
+
// MΓ³dulos Internos
|
|
10
|
+
const { AgentSchema } = require('./lib/schema');
|
|
11
|
+
const { toGeminiTOML, toRooConfig, toKiloMarkdown } = require('./lib/transformers');
|
|
12
|
+
const { generateWorkflowGuide } = require('./lib/docs');
|
|
13
|
+
|
|
14
|
+
async function main() {
|
|
15
|
+
console.clear();
|
|
16
|
+
intro(pc.bgMagenta(pc.white(' UNIVERSAL SPEC CLI ')));
|
|
17
|
+
|
|
18
|
+
// 1. SeleΓ§Γ£o de Componentes
|
|
19
|
+
const components = await multiselect({
|
|
20
|
+
message: 'O que vocΓͺ deseja configurar?',
|
|
21
|
+
options: [
|
|
22
|
+
{ value: 'docs', label: 'Gerar DocumentaΓ§Γ£o de Workflow (docs/README.md)', hint: 'Essencial' },
|
|
23
|
+
{ value: 'agents', label: 'Instalar Agentes de IA', hint: 'Recomendado' },
|
|
24
|
+
{ value: 'vscode', label: 'Configurar VS Code', hint: '(Simulado)' },
|
|
25
|
+
],
|
|
26
|
+
required: true,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
if (!components) {
|
|
30
|
+
outro('OperaΓ§Γ£o cancelada.');
|
|
31
|
+
process.exit(0);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// 2. InstalaΓ§Γ£o de DocumentaΓ§Γ£o
|
|
35
|
+
if (components.includes('docs')) {
|
|
36
|
+
const created = generateWorkflowGuide(process.cwd());
|
|
37
|
+
if (created) {
|
|
38
|
+
note('DocumentaΓ§Γ£o criada em docs/README.md', 'Docs');
|
|
39
|
+
} else {
|
|
40
|
+
console.log(pc.gray('βΉοΈ Pasta docs/ jΓ‘ existe. Ignorando criaΓ§Γ£o.'));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// 3. InstalaΓ§Γ£o de Agentes
|
|
45
|
+
if (components.includes('agents')) {
|
|
46
|
+
const tool = await select({
|
|
47
|
+
message: 'Onde vocΓͺ deseja instalar os Agentes?',
|
|
48
|
+
options: [
|
|
49
|
+
{ value: 'gemini', label: 'Gemini CLI', hint: '.gemini/commands/dev' },
|
|
50
|
+
{ value: 'roo', label: 'Roo Code', hint: 'Gera roo_custom_modes.json' },
|
|
51
|
+
{ value: 'cline', label: 'Cline', hint: 'Gera cline_custom_modes.json' },
|
|
52
|
+
{ value: 'kilo', label: 'Kilo Code', hint: '.kilo/prompts/*.md' },
|
|
53
|
+
],
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
if (!tool) process.exit(0);
|
|
57
|
+
|
|
58
|
+
await processAgentsInstallation(tool);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
outro(pc.green('ConfiguraΓ§Γ£o concluΓda com sucesso! π'));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function processAgentsInstallation(tool) {
|
|
65
|
+
const s = spinner();
|
|
66
|
+
s.start('Carregando definiΓ§Γ΅es...');
|
|
67
|
+
|
|
68
|
+
const definitionsDir = path.join(__dirname, '..', 'definitions');
|
|
69
|
+
if (!fs.existsSync(definitionsDir)) {
|
|
70
|
+
s.stop('Falha');
|
|
71
|
+
note(`Pasta de definiΓ§Γ΅es nΓ£o encontrada: ${definitionsDir}`, 'Erro Fatal');
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const files = fs.readdirSync(definitionsDir).filter(f => f.endsWith('.yaml') || f.endsWith('.yml'));
|
|
76
|
+
const validAgents = [];
|
|
77
|
+
|
|
78
|
+
// ValidaΓ§Γ£o e Carregamento
|
|
79
|
+
for (const file of files) {
|
|
80
|
+
try {
|
|
81
|
+
const content = fs.readFileSync(path.join(definitionsDir, file), 'utf8');
|
|
82
|
+
const raw = yaml.load(content);
|
|
83
|
+
|
|
84
|
+
// ValidaΓ§Γ£o com Zod
|
|
85
|
+
const parsed = AgentSchema.safeParse(raw);
|
|
86
|
+
if (!parsed.success) {
|
|
87
|
+
console.warn(pc.yellow(`β οΈ Ignorando ${file}: InvΓ‘lido`));
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const agent = parsed.data;
|
|
92
|
+
agent.slug = file.replace(/\.ya?ml$/, '').replace(/\./g, '-'); // dev.coder -> dev-coder
|
|
93
|
+
validAgents.push(agent);
|
|
94
|
+
|
|
95
|
+
} catch (e) {
|
|
96
|
+
console.error(pc.red(`Erro ao ler ${file}: ${e.message}`));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
s.message(`Instalando ${validAgents.length} agentes para ${tool}...`);
|
|
101
|
+
|
|
102
|
+
// InstalaΓ§Γ£o EspecΓfica por Ferramenta
|
|
103
|
+
try {
|
|
104
|
+
if (tool === 'gemini') {
|
|
105
|
+
const targetDir = path.join(process.cwd(), '.gemini', 'commands', 'dev');
|
|
106
|
+
if (!fs.existsSync(targetDir)) fs.mkdirSync(targetDir, { recursive: true });
|
|
107
|
+
|
|
108
|
+
for (const agent of validAgents) {
|
|
109
|
+
const toml = toGeminiTOML(agent);
|
|
110
|
+
// Nome original com pontos (dev.coder.toml) Γ© preferΓvel para Gemini CLI
|
|
111
|
+
const fileName = `${agent.slug.replace(/-/g, '.')}.toml`;
|
|
112
|
+
fs.writeFileSync(path.join(targetDir, fileName), toml);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
else if (tool === 'roo' || tool === 'cline') {
|
|
116
|
+
const modes = validAgents.map(agent => toRooConfig(agent, agent.slug));
|
|
117
|
+
const jsonContent = JSON.stringify({ customModes: modes }, null, 2);
|
|
118
|
+
const fileName = `${tool}_custom_modes.json`;
|
|
119
|
+
fs.writeFileSync(path.join(process.cwd(), fileName), jsonContent);
|
|
120
|
+
note(`Copie o conteΓΊdo de '${fileName}' para as configuraΓ§Γ΅es da extensΓ£o.`, 'AΓ§Γ£o Manual');
|
|
121
|
+
}
|
|
122
|
+
else if (tool === 'kilo') {
|
|
123
|
+
const targetDir = path.join(process.cwd(), '.kilo', 'prompts');
|
|
124
|
+
if (!fs.existsSync(targetDir)) fs.mkdirSync(targetDir, { recursive: true });
|
|
125
|
+
|
|
126
|
+
for (const agent of validAgents) {
|
|
127
|
+
const md = toKiloMarkdown(agent);
|
|
128
|
+
fs.writeFileSync(path.join(targetDir, `${agent.slug}.md`), md);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
s.stop('InstalaΓ§Γ£o finalizada!');
|
|
132
|
+
|
|
133
|
+
} catch (e) {
|
|
134
|
+
s.stop('Falha');
|
|
135
|
+
console.error(pc.red(e.message));
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
main().catch(console.error);
|
package/src/lib/docs.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const pc = require('picocolors');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Gera o guia de workflow do projeto
|
|
7
|
+
*/
|
|
8
|
+
function generateWorkflowGuide(baseDir) {
|
|
9
|
+
const docsDir = path.join(baseDir, 'docs');
|
|
10
|
+
|
|
11
|
+
// ConteΓΊdo baseado no exemplo provided
|
|
12
|
+
const content = `# π€ Agent Workflow Guide
|
|
13
|
+
|
|
14
|
+
Este documento descreve o fluxo de desenvolvimento padrΓ£o usando os Agentes instalados.
|
|
15
|
+
O sistema segue um processo **Waterfall** para planejamento (precisΓ£o) e **Iterativo** para execuΓ§Γ£o.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 1. ποΈ Project Spec (@Project Architect)
|
|
20
|
+
**Role:** O VisionΓ‘rio.
|
|
21
|
+
**Goal:** Traduzir sua ideia vaga em uma EspecificaΓ§Γ£o concreta com "Project Principles" definidos.
|
|
22
|
+
- **Comando:** \
|
|
23
|
+
/dev:project "Eu quero um App de Todo que..."
|
|
24
|
+
- **SaΓda:**
|
|
25
|
+
`docs/project.md`
|
|
26
|
+
|
|
27
|
+
## 2. π§± Requirements Engineering (@Requirements Engineer)
|
|
28
|
+
**Role:** O Tech Lead.
|
|
29
|
+
**Goal:** Fechar decisΓ΅es tΓ©cnicas (Stack, Banco de Dados, Libs).
|
|
30
|
+
- **Why?** Evita que o Coder "invente" arquitetura. Cria o "Contrato".
|
|
31
|
+
- **Comando:**
|
|
32
|
+
/dev:requirements
|
|
33
|
+
- **SaΓda:**
|
|
34
|
+
`docs/requirements.md`
|
|
35
|
+
|
|
36
|
+
## 3. πΊοΈ Roadmap Strategy (@Milestone Manager)
|
|
37
|
+
**Role:** O Estrategista.
|
|
38
|
+
**Goal:** Fatiar o projeto em fases de entrega (MVPs).
|
|
39
|
+
- **Comando:**
|
|
40
|
+
/dev:milestone
|
|
41
|
+
- **SaΓda:**
|
|
42
|
+
`docs/milestones.md`
|
|
43
|
+
|
|
44
|
+
## 4. π Task Planning (@Task Planner)
|
|
45
|
+
**Role:** O Gerente.
|
|
46
|
+
**Goal:** Quebrar um Milestone especΓfico em tarefas atΓ΄micas para desenvolvedores.
|
|
47
|
+
- **Why?** IAs falham com contextos gigantes. Tarefas pequenas = CΓ³digo perfeito.
|
|
48
|
+
- **Comando:**
|
|
49
|
+
/dev:tasks <Milestone_ID>
|
|
50
|
+
- **SaΓda:**
|
|
51
|
+
`docs/task.md`
|
|
52
|
+
|
|
53
|
+
## 5. π΅οΈ Blueprint Audit (@Auditor)
|
|
54
|
+
**Role:** O GuardiΓ£o.
|
|
55
|
+
**Goal:** Validar consistΓͺncia entre **Requirements** e **Tasks**.
|
|
56
|
+
- **Comando:**
|
|
57
|
+
/dev:auditor
|
|
58
|
+
- **SaΓda:**
|
|
59
|
+
`audit_report.md`
|
|
60
|
+
|
|
61
|
+
## 6. π» Implementation (@Coder)
|
|
62
|
+
**Role:** O Construtor.
|
|
63
|
+
**Goal:** Executar *uma tarefa por vez* do arquivo
|
|
64
|
+
`task.md`.
|
|
65
|
+
- **Comando:**
|
|
66
|
+
/dev:coder <Task_ID>
|
|
67
|
+
- **Buffer:**
|
|
68
|
+
`work_log.md`
|
|
69
|
+
|
|
70
|
+
## 7. βοΈ Quality Assurance (@QA Engineer)
|
|
71
|
+
**Role:** O Inspetor.
|
|
72
|
+
**Goal:** Verificar se a implementaΓ§Γ£o bate com os Requisitos.
|
|
73
|
+
- **Comando:**
|
|
74
|
+
/dev:review <Task_ID>
|
|
75
|
+
- **SaΓda:**
|
|
76
|
+
`docs/logs/review_log.md`
|
|
77
|
+
|
|
78
|
+
## 8. π¦ Release Management (@Release Manager)
|
|
79
|
+
**Role:** O Historiador.
|
|
80
|
+
**Goal:** Consolidar o
|
|
81
|
+
`work_log.md` em um
|
|
82
|
+
`changelog.md` permanente.
|
|
83
|
+
- **Comando:**
|
|
84
|
+
/dev:log
|
|
85
|
+
- **SaΓda:**
|
|
86
|
+
`changelog.md`
|
|
87
|
+
`;
|
|
88
|
+
|
|
89
|
+
if (!fs.existsSync(docsDir)) {
|
|
90
|
+
fs.mkdirSync(docsDir, { recursive: true });
|
|
91
|
+
fs.writeFileSync(path.join(docsDir, 'README.md'), content);
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
module.exports = { generateWorkflowGuide };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const { z } = require('zod');
|
|
2
|
+
|
|
3
|
+
const AgentSchema = z.object({
|
|
4
|
+
name: z.string().min(1, "Nome Γ© obrigatΓ³rio"),
|
|
5
|
+
role: z.string().min(1, "Papel (Role) Γ© obrigatΓ³rio"),
|
|
6
|
+
emoji: z.string().optional().default('π€'),
|
|
7
|
+
systemPrompt: z.string().min(10, "System Prompt deve ter pelo menos 10 caracteres"),
|
|
8
|
+
rules: z.array(z.string()).optional().default([]),
|
|
9
|
+
tools: z.array(z.string()).optional().default([]),
|
|
10
|
+
description: z.string().optional()
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
module.exports = { AgentSchema };
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converte definiΓ§Γ£o do agente para TOML do Gemini CLI
|
|
3
|
+
*/
|
|
4
|
+
function toGeminiTOML(agent) {
|
|
5
|
+
// Escapa aspas duplas na descriΓ§Γ£o
|
|
6
|
+
const description = (agent.description || agent.role).replace(/"/g, '\\"');
|
|
7
|
+
|
|
8
|
+
// ConstrΓ³i o prompt completo
|
|
9
|
+
const parts = [
|
|
10
|
+
`# Identity`,
|
|
11
|
+
`You are **${agent.name}** ${agent.emoji}`,
|
|
12
|
+
`Role: ${agent.role}\n`,
|
|
13
|
+
`# Core Instructions`,
|
|
14
|
+
agent.systemPrompt.trim(),
|
|
15
|
+
'\n'
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
if (agent.rules && agent.rules.length > 0) {
|
|
19
|
+
parts.push(`# Rules & Guidelines`);
|
|
20
|
+
agent.rules.forEach(rule => parts.push(`- ${rule}`));
|
|
21
|
+
parts.push('\n');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const fullPrompt = parts.join('\n');
|
|
25
|
+
|
|
26
|
+
// Escapa aspas triplas para o bloco multilinha TOML
|
|
27
|
+
const escapedPrompt = fullPrompt.replace(/"""/g, '\\"\\\"\\"');
|
|
28
|
+
|
|
29
|
+
// Monta o TOML final
|
|
30
|
+
let toml = `description = "${description}"\n`;
|
|
31
|
+
toml += `prompt = """\n${escapedPrompt}\n"""\n`;
|
|
32
|
+
|
|
33
|
+
// MantΓ©m rules como array separado se a ferramenta suportar (Gemini CLI suporta)
|
|
34
|
+
if (agent.rules && agent.rules.length > 0) {
|
|
35
|
+
toml += 'rules = [\n';
|
|
36
|
+
agent.rules.forEach(rule => {
|
|
37
|
+
const escaped = rule.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
38
|
+
toml += ` "${escaped}",\n`;
|
|
39
|
+
});
|
|
40
|
+
toml += ']\n';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return toml;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Converte para configuraΓ§Γ£o de Custom Mode do Roo Code / Cline (JSON)
|
|
48
|
+
*/
|
|
49
|
+
function toRooConfig(agent, slug) {
|
|
50
|
+
const promptParts = [
|
|
51
|
+
`# ${agent.name} (${agent.role})`,
|
|
52
|
+
`\n${agent.systemPrompt.trim()}\n`
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
if (agent.rules && agent.rules.length > 0) {
|
|
56
|
+
promptParts.push(`## Rules & Guidelines`);
|
|
57
|
+
agent.rules.forEach(rule => promptParts.push(`- ${rule}`));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
slug: slug,
|
|
62
|
+
name: `${agent.emoji} ${agent.name}`,
|
|
63
|
+
roleDefinition: promptParts.join('\n'),
|
|
64
|
+
groups: ["read", "edit", "browser", "command", "mcp"]
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Converte para Markdown do Kilo Code
|
|
70
|
+
*/
|
|
71
|
+
function toKiloMarkdown(agent) {
|
|
72
|
+
const parts = [
|
|
73
|
+
`<!--- Kilo Code Agent Config --->`,
|
|
74
|
+
`# ${agent.name} ${agent.emoji}`,
|
|
75
|
+
`**Role**: ${agent.role}\n`,
|
|
76
|
+
`## Instructions`,
|
|
77
|
+
agent.systemPrompt.trim(),
|
|
78
|
+
'\n'
|
|
79
|
+
];
|
|
80
|
+
|
|
81
|
+
if (agent.rules && agent.rules.length > 0) {
|
|
82
|
+
parts.push(`## Constraints`);
|
|
83
|
+
agent.rules.forEach(rule => parts.push(`- ${rule}`));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return parts.join('\n');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
module.exports = { toGeminiTOML, toRooConfig, toKiloMarkdown };
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.generateAgents = generateAgents;
|
|
7
|
-
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
8
|
-
const path_1 = __importDefault(require("path"));
|
|
9
|
-
const yaml_1 = __importDefault(require("yaml"));
|
|
10
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
11
|
-
const types_1 = require("../types");
|
|
12
|
-
const gemini_1 = require("../transformers/gemini");
|
|
13
|
-
const roo_1 = require("../transformers/roo");
|
|
14
|
-
const kilo_1 = require("../transformers/kilo");
|
|
15
|
-
const opencode_1 = require("../transformers/opencode");
|
|
16
|
-
const markdown_1 = require("../parsers/markdown");
|
|
17
|
-
async function generateAgents(definitionsDir, outDir, selectedTargets) {
|
|
18
|
-
console.log(chalk_1.default.blue(`\nπ¦ Building agents from ${definitionsDir}...`));
|
|
19
|
-
try {
|
|
20
|
-
if (outDir !== process.cwd()) {
|
|
21
|
-
await fs_extra_1.default.ensureDir(outDir);
|
|
22
|
-
}
|
|
23
|
-
// Check if input exists
|
|
24
|
-
if (!await fs_extra_1.default.pathExists(definitionsDir)) {
|
|
25
|
-
console.error(chalk_1.default.red(`Input not found: ${definitionsDir}`));
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
let files = [];
|
|
29
|
-
let baseDir = definitionsDir;
|
|
30
|
-
const stats = await fs_extra_1.default.stat(definitionsDir);
|
|
31
|
-
if (stats.isFile()) {
|
|
32
|
-
baseDir = path_1.default.dirname(definitionsDir);
|
|
33
|
-
files = [path_1.default.basename(definitionsDir)];
|
|
34
|
-
}
|
|
35
|
-
else {
|
|
36
|
-
files = await fs_extra_1.default.readdir(definitionsDir);
|
|
37
|
-
}
|
|
38
|
-
for (const file of files) {
|
|
39
|
-
const filePath = path_1.default.join(baseDir, file);
|
|
40
|
-
// 1. Handle YAML
|
|
41
|
-
if (file.endsWith('.yaml') || file.endsWith('.yml')) {
|
|
42
|
-
const content = await fs_extra_1.default.readFile(filePath, 'utf-8');
|
|
43
|
-
try {
|
|
44
|
-
const rawAgent = yaml_1.default.parse(content);
|
|
45
|
-
const parseResult = types_1.AgentSchema.safeParse(rawAgent);
|
|
46
|
-
if (!parseResult.success) {
|
|
47
|
-
logValidationErrors(file, parseResult.error);
|
|
48
|
-
continue;
|
|
49
|
-
}
|
|
50
|
-
// Use filename as slug for YAML (backward compatibility)
|
|
51
|
-
const slug = file.replace(/\.(yaml|yml)$/, '');
|
|
52
|
-
await buildAgentArtifacts(parseResult.data, slug, outDir, selectedTargets);
|
|
53
|
-
}
|
|
54
|
-
catch (e) {
|
|
55
|
-
console.error(chalk_1.default.red(`Error parsing YAML ${file}: ${e.message}`));
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
// 2. Handle Markdown
|
|
59
|
-
else if (file.endsWith('.md')) {
|
|
60
|
-
const content = await fs_extra_1.default.readFile(filePath, 'utf-8');
|
|
61
|
-
const agents = (0, markdown_1.parseMarkdownAgents)(content);
|
|
62
|
-
if (agents.length === 0) {
|
|
63
|
-
console.warn(chalk_1.default.yellow(`β οΈ No agents found in ${file}`));
|
|
64
|
-
continue;
|
|
65
|
-
}
|
|
66
|
-
for (const agent of agents) {
|
|
67
|
-
const parseResult = types_1.AgentSchema.safeParse(agent);
|
|
68
|
-
if (!parseResult.success) {
|
|
69
|
-
logValidationErrors(`${file} -> ${agent.name}`, parseResult.error);
|
|
70
|
-
continue;
|
|
71
|
-
}
|
|
72
|
-
// Use agent name as slug for Markdown
|
|
73
|
-
const slug = toSlug(agent.name);
|
|
74
|
-
await buildAgentArtifacts(agent, slug, outDir, selectedTargets);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
console.log(chalk_1.default.bold.green('\nπ Build complete! Agents are ready to use.'));
|
|
79
|
-
}
|
|
80
|
-
catch (err) {
|
|
81
|
-
console.error(chalk_1.default.red('Build failed:'), err);
|
|
82
|
-
process.exit(1);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
async function buildAgentArtifacts(agent, slug, outDir, selectedTargets) {
|
|
86
|
-
const allTargets = [
|
|
87
|
-
{
|
|
88
|
-
id: 'gemini',
|
|
89
|
-
subDir: '.gemini/commands',
|
|
90
|
-
ext: 'toml',
|
|
91
|
-
content: (0, gemini_1.toGeminiSystemPrompt)(agent),
|
|
92
|
-
name: `${slug}.toml`
|
|
93
|
-
},
|
|
94
|
-
{
|
|
95
|
-
id: 'roo',
|
|
96
|
-
subDir: '.roo/commands',
|
|
97
|
-
ext: 'md',
|
|
98
|
-
content: (0, roo_1.toRooRules)(agent),
|
|
99
|
-
name: `${slug}.md`
|
|
100
|
-
},
|
|
101
|
-
{
|
|
102
|
-
id: 'kilo',
|
|
103
|
-
subDir: '.kilocode/workflows',
|
|
104
|
-
ext: 'md',
|
|
105
|
-
content: (0, kilo_1.toKiloConfig)(agent),
|
|
106
|
-
name: `${slug}.md`
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
id: 'opencode',
|
|
110
|
-
subDir: '.opencode/command',
|
|
111
|
-
ext: 'md',
|
|
112
|
-
content: (0, opencode_1.toOpenCodeCommand)(agent),
|
|
113
|
-
name: `${slug}.md`
|
|
114
|
-
}
|
|
115
|
-
];
|
|
116
|
-
const targetsToBuild = allTargets.filter(t => selectedTargets.includes(t.id));
|
|
117
|
-
for (const target of targetsToBuild) {
|
|
118
|
-
const targetDir = path_1.default.join(outDir, target.subDir);
|
|
119
|
-
await fs_extra_1.default.ensureDir(targetDir);
|
|
120
|
-
await fs_extra_1.default.writeFile(path_1.default.join(targetDir, target.name), target.content);
|
|
121
|
-
console.log(chalk_1.default.gray(` -> ${target.subDir}/${target.name}`));
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
function logValidationErrors(source, error) {
|
|
125
|
-
console.error(chalk_1.default.red(`β Validation failed for ${source}:`));
|
|
126
|
-
error.issues.forEach((err) => {
|
|
127
|
-
console.error(chalk_1.default.red(` - ${err.path.join('.')}: ${err.message}`));
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
function toSlug(name) {
|
|
131
|
-
return name.toLowerCase()
|
|
132
|
-
.replace(/[^\w\s-]/g, '') // remove non-word chars
|
|
133
|
-
.replace(/[\s_-]+/g, '-') // collapse whitespace/underscores to hyphens
|
|
134
|
-
.replace(/^-+|-+$/g, ''); // trim hyphens
|
|
135
|
-
}
|
package/dist/generators/docs.js
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.generateWorkflowGuide = generateWorkflowGuide;
|
|
7
|
-
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
8
|
-
const path_1 = __importDefault(require("path"));
|
|
9
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
-
async function generateWorkflowGuide(docsDir, shell) {
|
|
11
|
-
const readmeContent = `# π€ Agent Workflow Guide
|
|
12
|
-
|
|
13
|
-
This document outlines the standard development flow using the installed Agents.
|
|
14
|
-
The system follows a **Waterfall-like** process relative to planning (to ensure precision) and an **Iterative** process for execution.
|
|
15
|
-
|
|
16
|
-
---
|
|
17
|
-
|
|
18
|
-
## 1. ποΈ Project Spec (@Project Architect)
|
|
19
|
-
**Role:** The Visionary.
|
|
20
|
-
**Goal:** Translate your vague idea into a concrete Specification with defined "Project Principles" (Constitution).
|
|
21
|
-
- **Why?** To ensure the machine understands the "Soul" of the project (e.g., "Mobile First", "Minimalist").
|
|
22
|
-
- **Command:** \`/dev:project "I want a Todo App that..."\`
|
|
23
|
-
- **Output:** \`docs/project.md\`
|
|
24
|
-
|
|
25
|
-
## 2. π§± Requirements Engineering (@Requirements Engineer)
|
|
26
|
-
**Role:** The Tech Lead.
|
|
27
|
-
**Goal:** Lock down technical decisions (Stack, Database, Libraries) based on the Spec.
|
|
28
|
-
- **Why?** To prevent the Coder from "inventing" architecture on the fly. It creates a "Contract" of what to build.
|
|
29
|
-
- **Command:** \`/dev:requirements\`
|
|
30
|
-
- **Output:** \`docs/requirements.md\` (The Technical Contract)
|
|
31
|
-
|
|
32
|
-
## 3. πΊοΈ Roadmap Strategy (@Milestone Manager)
|
|
33
|
-
**Role:** The Strategist.
|
|
34
|
-
**Goal:** Slice the project into logical delivery phases (MVPs).
|
|
35
|
-
- **Why?** To avoid "Big Bang" development. It organizes work into sequential milestones (e.g., "M1: Auth", "M2: Dashboard").
|
|
36
|
-
- **Command:** \`/dev:milestone\`
|
|
37
|
-
- **Output:** \`docs/milestones.md\`
|
|
38
|
-
|
|
39
|
-
## 4. π Task Planning (@Task Planner)
|
|
40
|
-
**Role:** The Manager.
|
|
41
|
-
**Goal:** Break down a specific Milestone into atomic, developer-ready tasks.
|
|
42
|
-
- **Why?** AI coders fail with large contexts. Small, clear tasks = Perfect code.
|
|
43
|
-
- **Command:** \`/dev:tasks <Milestone_ID>\`
|
|
44
|
-
- **Output:** \`docs/task.md\`
|
|
45
|
-
|
|
46
|
-
## 5. π΅οΈ Blueprint Audit (@Auditor)
|
|
47
|
-
**Role:** The Gatekeeper.
|
|
48
|
-
**Goal:** Validate consistency between **Requirements** (The Contract) and **Tasks** (The Plan).
|
|
49
|
-
- **Why?** To catch missing requirements or dangerous hallucinations *before* a single line of code is written.
|
|
50
|
-
- **Command:** \`/dev:auditor\`
|
|
51
|
-
- **Output:** \`audit_report.md\`
|
|
52
|
-
|
|
53
|
-
## 6. π» Implementation (@Coder)
|
|
54
|
-
**Role:** The Builder.
|
|
55
|
-
**Goal:** Execute *one task at a time* from the \`task.md\` file.
|
|
56
|
-
- **Why?** Focus. It reads the docs, writes the code (and tests), and logs progress.
|
|
57
|
-
- **Safeguards:** Checks for \`.gitignore\` and strictly follows \`requirements.md\`.
|
|
58
|
-
- **Command:** \`/dev:coder <Task_ID>\`
|
|
59
|
-
- **Buffer:** \`work_log.md\`
|
|
60
|
-
|
|
61
|
-
## 7. βοΈ Quality Assurance (@QA Engineer)
|
|
62
|
-
**Role:** The Inspector.
|
|
63
|
-
**Goal:** Verify if the implementation matches the Requirement Artifacts.
|
|
64
|
-
- **Why?** Trust but verify. It reads the \`work_log.md\` and checks against expectations.
|
|
65
|
-
- **Command:** \`/dev:review <Task_ID>\`
|
|
66
|
-
- **Output:** \`docs/logs/review_log.md\`
|
|
67
|
-
|
|
68
|
-
## 8. π¦ Release Management (@Release Manager)
|
|
69
|
-
**Role:** The Historian.
|
|
70
|
-
**Goal:** Consolidate the temporary \`work_log.md\` into a permanent \`changelog.md\`.
|
|
71
|
-
- **Why?** To clean up the workspace and keep a professional history of the project.
|
|
72
|
-
- **Command:** \`/dev:log\`
|
|
73
|
-
- **Output:** \`changelog.md\`
|
|
74
|
-
|
|
75
|
-
---
|
|
76
|
-
|
|
77
|
-
## π οΈ On-Demand Utilities
|
|
78
|
-
|
|
79
|
-
### Infrastructure (@DevOps Engineer)
|
|
80
|
-
**Role:** The Mechanic.
|
|
81
|
-
**Goal:** Handle "Config Hell" (Docker, CI/CD, Linters).
|
|
82
|
-
- **Why?** Keeps the Coder focused on business logic.
|
|
83
|
-
- **Command:** \`/dev:ops\`
|
|
84
|
-
`;
|
|
85
|
-
await fs_extra_1.default.ensureDir(docsDir);
|
|
86
|
-
await fs_extra_1.default.writeFile(path_1.default.join(docsDir, 'README.md'), readmeContent);
|
|
87
|
-
console.log(chalk_1.default.green(`β
Created "docs/" directory (Optimized for ${shell}).\n`));
|
|
88
|
-
console.log(chalk_1.default.green(` -> Generated "docs/README.md" with workflow instructions.\n`));
|
|
89
|
-
}
|
package/dist/index.js
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
-
};
|
|
6
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
const commander_1 = require("commander");
|
|
8
|
-
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
9
|
-
const path_1 = __importDefault(require("path"));
|
|
10
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
11
|
-
const inquirer_1 = __importDefault(require("inquirer"));
|
|
12
|
-
const docs_1 = require("./generators/docs");
|
|
13
|
-
const agents_1 = require("./generators/agents");
|
|
14
|
-
const program = new commander_1.Command();
|
|
15
|
-
program
|
|
16
|
-
.name('agents')
|
|
17
|
-
.description('Agent Installer CLI')
|
|
18
|
-
.version('1.0.0')
|
|
19
|
-
.option('-o, --out <dir>', 'Output directory', '.')
|
|
20
|
-
.option('-i, --input <path>', 'Input file or directory (default: definitions/ or agents.md)')
|
|
21
|
-
.option('-t, --target <targets>', 'Comma-separated target formats (gemini, roo, kilo, opencode)')
|
|
22
|
-
.action(async (options) => {
|
|
23
|
-
console.log(chalk_1.default.bold.blue('\nπ Agents.dev Installer Wizard\n'));
|
|
24
|
-
// --- STEP 1: INITIALIZATION (Docs & Shell) ---
|
|
25
|
-
const docsDir = path_1.default.join(process.cwd(), 'docs');
|
|
26
|
-
// Check if docs directory exists
|
|
27
|
-
if (!await fs_extra_1.default.pathExists(docsDir)) {
|
|
28
|
-
console.log(chalk_1.default.yellow('βΉοΈ Project documentation structure not found.'));
|
|
29
|
-
const initAnswers = await inquirer_1.default.prompt([
|
|
30
|
-
{
|
|
31
|
-
type: 'list',
|
|
32
|
-
name: 'shell',
|
|
33
|
-
message: 'Initialize validation: Which shell do you use?',
|
|
34
|
-
choices: [
|
|
35
|
-
{ name: 'Windows (CMD/PowerShell)', value: 'win32' },
|
|
36
|
-
{ name: 'Unix-like (Bash/Zsh/Ubuntu)', value: 'unix' }
|
|
37
|
-
]
|
|
38
|
-
}
|
|
39
|
-
]);
|
|
40
|
-
await (0, docs_1.generateWorkflowGuide)(docsDir, initAnswers.shell);
|
|
41
|
-
}
|
|
42
|
-
else {
|
|
43
|
-
console.log(chalk_1.default.gray('β
"docs/" directory already exists.\n'));
|
|
44
|
-
}
|
|
45
|
-
// --- STEP 2: BUILD AGENTS ---
|
|
46
|
-
let inputPath = options.input ? path_1.default.resolve(options.input) : path_1.default.join(process.cwd(), 'definitions');
|
|
47
|
-
// If default definitions dir doesn't exist and no input specified, try agents.md
|
|
48
|
-
if (!options.input && !await fs_extra_1.default.pathExists(inputPath)) {
|
|
49
|
-
const localAgentsMd = path_1.default.join(process.cwd(), 'agents.md');
|
|
50
|
-
if (await fs_extra_1.default.pathExists(localAgentsMd)) {
|
|
51
|
-
inputPath = localAgentsMd;
|
|
52
|
-
console.log(chalk_1.default.gray(`βΉοΈ Using 'agents.md' found in root.`));
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
const outDir = path_1.default.resolve(options.out);
|
|
56
|
-
// Determine targets
|
|
57
|
-
let selectedTargets = [];
|
|
58
|
-
if (options.target) {
|
|
59
|
-
selectedTargets = options.target.split(',').map((t) => t.trim().toLowerCase());
|
|
60
|
-
}
|
|
61
|
-
else {
|
|
62
|
-
const buildAnswers = await inquirer_1.default.prompt([
|
|
63
|
-
{
|
|
64
|
-
type: 'checkbox',
|
|
65
|
-
name: 'targets',
|
|
66
|
-
message: 'Select target formats to build:',
|
|
67
|
-
choices: [
|
|
68
|
-
{ name: 'Gemini CLI', value: 'gemini', checked: true },
|
|
69
|
-
{ name: 'Roo Code', value: 'roo', checked: false },
|
|
70
|
-
{ name: 'Kilo Code', value: 'kilo', checked: false },
|
|
71
|
-
{ name: 'OpenCode', value: 'opencode', checked: false }
|
|
72
|
-
],
|
|
73
|
-
validate: (answer) => {
|
|
74
|
-
if (answer.length < 1) {
|
|
75
|
-
return 'You must choose at least one target.';
|
|
76
|
-
}
|
|
77
|
-
return true;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
]);
|
|
81
|
-
selectedTargets = buildAnswers.targets;
|
|
82
|
-
}
|
|
83
|
-
if (selectedTargets.length === 0) {
|
|
84
|
-
console.log(chalk_1.default.yellow('No targets selected. Exiting.'));
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
await (0, agents_1.generateAgents)(inputPath, outDir, selectedTargets);
|
|
88
|
-
});
|
|
89
|
-
program.parse(process.argv);
|
package/dist/parsers/markdown.js
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.parseMarkdownAgents = parseMarkdownAgents;
|
|
4
|
-
function parseMarkdownAgents(content) {
|
|
5
|
-
const agents = [];
|
|
6
|
-
// Normalize line endings
|
|
7
|
-
const normalized = content.replace(/\r\n/g, '\n');
|
|
8
|
-
// Split by H1 headers (# Name)
|
|
9
|
-
// We use a lookahead to keep the delimiter or just split and ignore the first empty part if it starts with #
|
|
10
|
-
const parts = normalized.split(/^# /gm);
|
|
11
|
-
for (const part of parts) {
|
|
12
|
-
if (!part.trim())
|
|
13
|
-
continue;
|
|
14
|
-
const lines = part.split('\n');
|
|
15
|
-
const headerLine = lines[0].trim();
|
|
16
|
-
// Extract emoji if present at the end
|
|
17
|
-
// E.g. "Project Manager π©βπΌ" -> name: "Project Manager", emoji: "π©βπΌ"
|
|
18
|
-
// Simple emoji regex or just take the last char if it looks like an emoji?
|
|
19
|
-
// Let's generic split by space and check if last part is emoji-like or just treat whole as name
|
|
20
|
-
// A simplified approach: regex for emoji is complex, let's just grab the whole string as name for now,
|
|
21
|
-
// or try to extract the last non-word character if it looks like a symbol.
|
|
22
|
-
// Spec said: "# Agent Name [Emoji]"
|
|
23
|
-
const emojiMatch = headerLine.match(/^(.*?)\s+([^\w\s\d]+)$/);
|
|
24
|
-
let name = headerLine;
|
|
25
|
-
let emoji = '';
|
|
26
|
-
if (emojiMatch) {
|
|
27
|
-
name = emojiMatch[1].trim();
|
|
28
|
-
emoji = emojiMatch[2].trim();
|
|
29
|
-
}
|
|
30
|
-
const remainingLines = lines.slice(1);
|
|
31
|
-
const sectionContent = remainingLines.join('\n');
|
|
32
|
-
// Parse sections
|
|
33
|
-
// > Role
|
|
34
|
-
// ## System Prompt
|
|
35
|
-
// ## Rules
|
|
36
|
-
// ## Tools
|
|
37
|
-
let role = '';
|
|
38
|
-
let systemPrompt = '';
|
|
39
|
-
const rules = [];
|
|
40
|
-
const tools = [];
|
|
41
|
-
// Extract Role (Blockquote)
|
|
42
|
-
const roleMatch = sectionContent.match(/^\s*>\s*(.+)$/m);
|
|
43
|
-
if (roleMatch) {
|
|
44
|
-
role = roleMatch[1].trim();
|
|
45
|
-
}
|
|
46
|
-
// specific sections
|
|
47
|
-
const sections = sectionContent.split(/^## /gm);
|
|
48
|
-
for (const section of sections) {
|
|
49
|
-
const trimmed = section.trim();
|
|
50
|
-
if (!trimmed)
|
|
51
|
-
continue;
|
|
52
|
-
const sectionLines = trimmed.split('\n');
|
|
53
|
-
const sectionTitle = sectionLines[0].trim().toLowerCase();
|
|
54
|
-
const sectionBody = sectionLines.slice(1).join('\n').trim();
|
|
55
|
-
if (sectionTitle.includes('system prompt') || sectionTitle.includes('instructions')) {
|
|
56
|
-
systemPrompt = sectionBody;
|
|
57
|
-
}
|
|
58
|
-
else if (sectionTitle.includes('rules')) {
|
|
59
|
-
// Extract bullets
|
|
60
|
-
const bullets = sectionBody.split('\n')
|
|
61
|
-
.map(l => l.trim())
|
|
62
|
-
.filter(l => l.startsWith('- ') || l.startsWith('* '))
|
|
63
|
-
.map(l => l.substring(2).trim());
|
|
64
|
-
rules.push(...bullets);
|
|
65
|
-
}
|
|
66
|
-
else if (sectionTitle.includes('tools')) {
|
|
67
|
-
const bullets = sectionBody.split('\n')
|
|
68
|
-
.map(l => l.trim())
|
|
69
|
-
.filter(l => l.startsWith('- ') || l.startsWith('* '))
|
|
70
|
-
.map(l => l.substring(2).trim());
|
|
71
|
-
tools.push(...bullets);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
if (name) {
|
|
75
|
-
agents.push({
|
|
76
|
-
name,
|
|
77
|
-
role,
|
|
78
|
-
emoji,
|
|
79
|
-
systemPrompt,
|
|
80
|
-
rules,
|
|
81
|
-
tools
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
return agents;
|
|
86
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.toGeminiSystemPrompt = toGeminiSystemPrompt;
|
|
4
|
-
function toGeminiSystemPrompt(agent) {
|
|
5
|
-
// Construct the prompt content (System Prompt + Rules + Tools)
|
|
6
|
-
const promptParts = [];
|
|
7
|
-
// Identity logic is usually part of the prompt in this TOML structure
|
|
8
|
-
promptParts.push(`# Identity`);
|
|
9
|
-
promptParts.push(`You are **${agent.name}** ${agent.emoji || ''}`);
|
|
10
|
-
promptParts.push(`Role: ${agent.role}\n`);
|
|
11
|
-
promptParts.push(`# Core Instructions`);
|
|
12
|
-
promptParts.push(agent.systemPrompt.trim());
|
|
13
|
-
promptParts.push('\n');
|
|
14
|
-
if (agent.rules && agent.rules.length > 0) {
|
|
15
|
-
promptParts.push(`# Rules & Guidelines`);
|
|
16
|
-
agent.rules.forEach(rule => promptParts.push(`- ${rule}`));
|
|
17
|
-
promptParts.push('\n');
|
|
18
|
-
}
|
|
19
|
-
if (agent.tools && agent.tools.length > 0) {
|
|
20
|
-
promptParts.push(`# Preferred Tools`);
|
|
21
|
-
agent.tools.forEach(tool => promptParts.push(`- ${tool}`));
|
|
22
|
-
promptParts.push('\n');
|
|
23
|
-
}
|
|
24
|
-
const fullPrompt = promptParts.join('\n');
|
|
25
|
-
// Manual TOML construction
|
|
26
|
-
// 1. description = "..."
|
|
27
|
-
// Basic escaping for double quotes
|
|
28
|
-
const escapedDescription = agent.role.replace(/"/g, '\\"');
|
|
29
|
-
// 2. prompt = """..."""
|
|
30
|
-
// Escape triple quotes if they exist in the content (rare but possible)
|
|
31
|
-
const escapedPrompt = fullPrompt.replace(/"""/g, '\\"\\"\\"');
|
|
32
|
-
return `description = "${escapedDescription}"
|
|
33
|
-
prompt = """
|
|
34
|
-
${escapedPrompt}
|
|
35
|
-
"""`;
|
|
36
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.toKiloConfig = toKiloConfig;
|
|
4
|
-
function toKiloConfig(agent) {
|
|
5
|
-
// Kilo Code generic config/prompt
|
|
6
|
-
const parts = [];
|
|
7
|
-
parts.push(`<!--- Kilo Code Agent Config --->`);
|
|
8
|
-
parts.push(`# ${agent.name} ${agent.emoji || ''}`);
|
|
9
|
-
parts.push(`**Role**: ${agent.role}\n`);
|
|
10
|
-
parts.push(`## Instructions`);
|
|
11
|
-
parts.push(agent.systemPrompt.trim());
|
|
12
|
-
parts.push('\n');
|
|
13
|
-
if (agent.rules && agent.rules.length > 0) {
|
|
14
|
-
parts.push(`## Constraints`);
|
|
15
|
-
agent.rules.forEach(rule => parts.push(`- ${rule}`));
|
|
16
|
-
}
|
|
17
|
-
return parts.join('\n');
|
|
18
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.toOpenCodeCommand = toOpenCodeCommand;
|
|
4
|
-
function toOpenCodeCommand(agent) {
|
|
5
|
-
// OpenCode generic config/prompt
|
|
6
|
-
const parts = [];
|
|
7
|
-
parts.push(`<!--- OpenCode Agent Config --->`);
|
|
8
|
-
parts.push(`# ${agent.name} ${agent.emoji || ''}`);
|
|
9
|
-
parts.push(`**Role**: ${agent.role}\n`);
|
|
10
|
-
parts.push(`## Instructions`);
|
|
11
|
-
parts.push(agent.systemPrompt.trim());
|
|
12
|
-
parts.push('\n');
|
|
13
|
-
if (agent.rules && agent.rules.length > 0) {
|
|
14
|
-
parts.push(`## Constraints`);
|
|
15
|
-
agent.rules.forEach(rule => parts.push(`- ${rule}`));
|
|
16
|
-
}
|
|
17
|
-
return parts.join('\n');
|
|
18
|
-
}
|
package/dist/transformers/roo.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.toRooRules = toRooRules;
|
|
4
|
-
function toRooRules(agent) {
|
|
5
|
-
// Roo Code / Cline usually expects a clear set of rules and role definition.
|
|
6
|
-
const parts = [];
|
|
7
|
-
parts.push(`# ${agent.name} (${agent.role})`);
|
|
8
|
-
parts.push(`\n${agent.systemPrompt.trim()}\n`);
|
|
9
|
-
if (agent.rules && agent.rules.length > 0) {
|
|
10
|
-
parts.push(`## Analytical Rules & Guidelines`);
|
|
11
|
-
agent.rules.forEach(rule => parts.push(`- ${rule}`));
|
|
12
|
-
parts.push('\n');
|
|
13
|
-
}
|
|
14
|
-
// Tools might be handled differently in Roo, but listing them as preferences is safe
|
|
15
|
-
if (agent.tools && agent.tools.length > 0) {
|
|
16
|
-
parts.push(`## Tool Usage Preferences`);
|
|
17
|
-
agent.tools.forEach(tool => parts.push(`- Prefer using ${tool} when applicable.`));
|
|
18
|
-
}
|
|
19
|
-
return parts.join('\n');
|
|
20
|
-
}
|
package/dist/types.js
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.AgentSchema = void 0;
|
|
4
|
-
const zod_1 = require("zod");
|
|
5
|
-
exports.AgentSchema = zod_1.z.object({
|
|
6
|
-
name: zod_1.z.string().min(1, "Name is required"),
|
|
7
|
-
role: zod_1.z.string().min(1, "Role is required"),
|
|
8
|
-
emoji: zod_1.z.string().optional(),
|
|
9
|
-
systemPrompt: zod_1.z.string().min(10, "System prompt must be at least 10 characters"),
|
|
10
|
-
rules: zod_1.z.array(zod_1.z.string()).optional(),
|
|
11
|
-
tools: zod_1.z.array(zod_1.z.string()).optional()
|
|
12
|
-
});
|