@naraya/cli 0.1.0 → 0.4.0

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 (83) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +394 -93
  3. package/bin/naraya-native.mjs +4 -0
  4. package/bin/naraya.mjs +1 -142
  5. package/bin/undici-timeout.mjs +1 -0
  6. package/dist/assets.pack.gz +0 -0
  7. package/dist/mcp/config-loader.js +32 -0
  8. package/dist/mcp/lifecycle.js +90 -0
  9. package/dist/mcp/tool-mapper.js +31 -0
  10. package/dist/mcp/transport.js +30 -0
  11. package/dist/pentest/catalog/catalog-loader.js +45 -0
  12. package/dist/pentest/catalog/index.js +1 -0
  13. package/dist/pentest/cli.js +117 -0
  14. package/dist/pentest/command-builder/command-builder.js +90 -0
  15. package/dist/pentest/command-builder/index.js +1 -0
  16. package/dist/pentest/index.js +10 -0
  17. package/dist/pentest/installer/index.js +1 -0
  18. package/dist/pentest/installer/tool-installer.js +90 -0
  19. package/dist/pentest/manager.js +125 -0
  20. package/dist/pentest/mode/index.js +1 -0
  21. package/dist/pentest/mode/mode-selector.js +127 -0
  22. package/dist/pentest/selector/index.js +1 -0
  23. package/dist/pentest/selector/tool-selector.js +66 -0
  24. package/dist/pentest/skill-bridge/index.js +1 -0
  25. package/dist/pentest/skill-bridge/skill-bridge.js +66 -0
  26. package/dist/pentest/skills/generator/index.js +1 -0
  27. package/dist/pentest/skills/generator/skill-generator.js +310 -0
  28. package/dist/pentest/skills/index.js +3 -0
  29. package/dist/pentest/skills/loader/index.js +1 -0
  30. package/dist/pentest/skills/loader/skill-loader.js +167 -0
  31. package/dist/pentest/skills/register/index.js +1 -0
  32. package/dist/pentest/skills/register/skill-register.js +162 -0
  33. package/dist/pentest/skills/types.js +1 -0
  34. package/dist/pentest/types.js +90 -0
  35. package/package.json +42 -14
  36. package/src/assets-pack.mjs +1 -0
  37. package/src/banner.mjs +5 -0
  38. package/src/clipboard.mjs +1 -0
  39. package/src/config.mjs +1 -40
  40. package/src/goodbye.mjs +7 -0
  41. package/src/login.mjs +7 -49
  42. package/src/mcp/config-loader.ts +50 -0
  43. package/src/mcp/lifecycle.ts +113 -0
  44. package/src/mcp/tool-mapper.ts +42 -0
  45. package/src/mcp/transport.ts +38 -0
  46. package/src/mcp-cli.mjs +5 -0
  47. package/src/pentest/catalog/catalog-loader.ts +55 -0
  48. package/src/pentest/catalog/index.ts +1 -0
  49. package/src/pentest/cli.ts +130 -0
  50. package/src/pentest/command-builder/command-builder.ts +109 -0
  51. package/src/pentest/command-builder/index.ts +1 -0
  52. package/src/pentest/index.ts +11 -0
  53. package/src/pentest/installer/index.ts +1 -0
  54. package/src/pentest/installer/tool-installer.ts +107 -0
  55. package/src/pentest/manager.ts +167 -0
  56. package/src/pentest/mode/index.ts +1 -0
  57. package/src/pentest/mode/mode-selector.ts +159 -0
  58. package/src/pentest/selector/index.ts +1 -0
  59. package/src/pentest/selector/tool-selector.ts +87 -0
  60. package/src/pentest/skill-bridge/index.ts +1 -0
  61. package/src/pentest/skill-bridge/skill-bridge.ts +86 -0
  62. package/src/pentest/skills/generator/index.ts +1 -0
  63. package/src/pentest/skills/generator/skill-generator.ts +373 -0
  64. package/src/pentest/skills/index.ts +4 -0
  65. package/src/pentest/skills/loader/index.ts +1 -0
  66. package/src/pentest/skills/loader/skill-loader.ts +206 -0
  67. package/src/pentest/skills/register/index.ts +1 -0
  68. package/src/pentest/skills/register/skill-register.ts +196 -0
  69. package/src/pentest/skills/types.ts +66 -0
  70. package/src/pentest/types.ts +341 -0
  71. package/src/seed.mjs +1 -36
  72. package/src/splash.mjs +4 -0
  73. package/src/status.mjs +2 -71
  74. package/assets/APPEND-SYSTEM.md +0 -9
  75. package/assets/extensions/naraya-brand.ts +0 -251
  76. package/assets/extensions/naraya-gate.ts +0 -23
  77. package/assets/naraya-logo.txt +0 -5
  78. package/assets/skills/narabuild/SKILL.md +0 -156
  79. package/assets/skills/naradroid/SKILL.md +0 -118
  80. package/assets/skills/naraexplore/SKILL.md +0 -71
  81. package/assets/skills/narafe/SKILL.md +0 -94
  82. package/assets/skills/naraplan/SKILL.md +0 -47
  83. package/assets/skills/narasearch/SKILL.md +0 -141
package/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Naraya CLI — Proprietary License
2
+ Copyright (c) 2026 PT. Naraya Teknologi Indonesia. All rights reserved.
3
+
4
+ This software and its accompanying assets (including but not limited to system
5
+ prompts, agent definitions, skills, and configuration) are the confidential and
6
+ proprietary property of PT. Naraya Teknologi Indonesia ("Naraya").
7
+
8
+ NO LICENSE IS GRANTED to copy, modify, distribute, sublicense, reverse engineer,
9
+ decompile, disassemble, or create derivative works from any part of this
10
+ software or its assets, in whole or in part, except as expressly authorized in
11
+ writing by Naraya.
12
+
13
+ Use of this software requires a valid Naraya account and API key and is governed
14
+ by the Naraya Terms of Service. Access may be revoked at any time.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
18
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL NARAYA BE LIABLE
19
+ FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md CHANGED
@@ -1,93 +1,394 @@
1
- # NaraCLI
2
-
3
-
4
-
5
- ## Getting started
6
-
7
- To make it easy for you to get started with GitLab, here's a list of recommended next steps.
8
-
9
- Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!
10
-
11
- ## Add your files
12
-
13
- * [Create](https://docs.gitlab.com/user/project/repository/web_editor/#create-a-file) or [upload](https://docs.gitlab.com/user/project/repository/web_editor/#upload-a-file) files
14
- * [Add files using the command line](https://docs.gitlab.com/topics/git/add_files/#add-files-to-a-git-repository) or push an existing Git repository with the following command:
15
-
16
- ```
17
- cd existing_repo
18
- git remote add origin https://gitlab.naraya.ai/adearman/naracli.git
19
- git branch -M main
20
- git push -uf origin main
21
- ```
22
-
23
- ## Integrate with your tools
24
-
25
- * [Set up project integrations](https://gitlab.naraya.ai/adearman/naracli/-/settings/integrations)
26
-
27
- ## Collaborate with your team
28
-
29
- * [Invite team members and collaborators](https://docs.gitlab.com/user/project/members/)
30
- * [Create a new merge request](https://docs.gitlab.com/user/project/merge_requests/creating_merge_requests/)
31
- * [Automatically close issues from merge requests](https://docs.gitlab.com/user/project/issues/managing_issues/#closing-issues-automatically)
32
- * [Enable merge request approvals](https://docs.gitlab.com/user/project/merge_requests/approvals/)
33
- * [Set auto-merge](https://docs.gitlab.com/user/project/merge_requests/auto_merge/)
34
-
35
- ## Test and Deploy
36
-
37
- Use the built-in continuous integration in GitLab.
38
-
39
- * [Get started with GitLab CI/CD](https://docs.gitlab.com/ci/quick_start/)
40
- * [Analyze your code for known vulnerabilities with Static Application Security Testing (SAST)](https://docs.gitlab.com/user/application_security/sast/)
41
- * [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/topics/autodevops/requirements/)
42
- * [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/user/clusters/agent/)
43
- * [Set up protected environments](https://docs.gitlab.com/ci/environments/protected_environments/)
44
-
45
- ***
46
-
47
- # Editing this README
48
-
49
- When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thanks to [makeareadme.com](https://www.makeareadme.com/) for this template.
50
-
51
- ## Suggestions for a good README
52
-
53
- Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
54
-
55
- ## Name
56
- Choose a self-explaining name for your project.
57
-
58
- ## Description
59
- Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
60
-
61
- ## Badges
62
- On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
63
-
64
- ## Visuals
65
- Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
66
-
67
- ## Installation
68
- Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
69
-
70
- ## Usage
71
- Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
72
-
73
- ## Support
74
- Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
75
-
76
- ## Roadmap
77
- If you have ideas for releases in the future, it is a good idea to list them in the README.
78
-
79
- ## Contributing
80
- State if you are open to contributions and what your requirements are for accepting them.
81
-
82
- For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
83
-
84
- You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
85
-
86
- ## Authors and acknowledgment
87
- Show your appreciation to those who have contributed to the project.
88
-
89
- ## License
90
- For open source projects, say how it is licensed.
91
-
92
- ## Project status
93
- If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
1
+ # Naraya CLI
2
+
3
+ > One sign-in, every model. A coding agent that delegates specialist work to focused subagents and brings evidence to every claim.
4
+
5
+ [![Node](https://img.shields.io/badge/node-%E2%89%A522.19-blue)](https://nodejs.org) [![License: MIT](https://img.shields.io/badge/license-MIT-green)](LICENSE) [![Version](https://img.shields.io/badge/version-0.2.4-blue)](package.json)
6
+
7
+ Naraya CLI is a thin wrapper around [pi](https://github.com/earendil-works/pi-mono) that:
8
+
9
+ - Routes all model traffic through [router.naraya.ai](https://router.naraya.ai) with one API key.
10
+ - Ships **11 focused subagents** (`nara-build`, `nara-architect`, `nara-release`, `nara-review`, `nara-debug`, `nara-plan`, `nara-explore`, `nara-search`, `nara-fe`, `nara-droid`, `nara-vision`) as folder-based packages with their own skills, scripts, and references.
11
+ - Injects a `APPEND-SYSTEM.md` so the model reaches for the bundled agents and skills on every turn.
12
+ - Provides a `delegate` tool that fans work out to subagents in parallel, sequential chains, or single focus — each in an isolated context window.
13
+
14
+ ---
15
+
16
+ ## Table of contents
17
+
18
+ - [Install](#install)
19
+ - [Quickstart](#quickstart)
20
+ - [Usage](#usage)
21
+ - [Subagents](#subagents)
22
+ - [Skills](#skills)
23
+ - [Agent structure](#agent-structure)
24
+ - [Extending](#extending)
25
+ - [Development](#development)
26
+ - [Architecture](#architecture)
27
+ - [License](#license)
28
+
29
+ ---
30
+
31
+ ## Install
32
+
33
+ ```bash
34
+ # Requires Node.js >= 22.19
35
+ npm install -g @naraya/cli
36
+ ```
37
+
38
+ Or use directly from a checkout:
39
+
40
+ ```bash
41
+ git clone https://gitlab.naraya.ai/adearman/naracli
42
+ cd naracli
43
+ npm install
44
+ npm link # expose `naraya` on PATH
45
+ ```
46
+
47
+ ---
48
+
49
+ ## Quickstart
50
+
51
+ ```bash
52
+ # 1. Sign in (one-time)
53
+ naraya login
54
+
55
+ # 2. Verify auth
56
+ naraya status
57
+
58
+ # 3. Launch the agent in the current project
59
+ naraya
60
+ ```
61
+
62
+ Inside the agent, press `/` for the command palette, `/agents` to list subagents, `/skills` to list skills.
63
+
64
+ ```bash
65
+ # Quick subcommands without launching the TUI
66
+ naraya login # OAuth-style sign-in to router.naraya.ai
67
+ naraya logout # wipe local credentials
68
+ naraya status # show auth + quota + current model
69
+ naraya status --usd # same, with USD cost equivalent
70
+ ```
71
+
72
+ ---
73
+
74
+ ## Usage
75
+
76
+ ### Interactive mode
77
+
78
+ ```bash
79
+ naraya # launch in cwd
80
+ naraya --model naraya/glm-5 # override default model for this session
81
+ naraya --continue # resume the last session
82
+ ```
83
+
84
+ Slash commands inside the TUI:
85
+
86
+ | Command | Effect |
87
+ |---|---|
88
+ | `/agents` | list subagents |
89
+ | `/agent-model <name>` | change a subagent's model |
90
+ | `/skill:<name> [args]` | load a skill |
91
+ | `/skills` | list available skills |
92
+ | `/help` | full command palette |
93
+
94
+ ### Headless / scripted
95
+
96
+ ```bash
97
+ naraya -p "audit the auth module for OWASP top 10" # single prompt, prints result, exits
98
+ naraya -p --json <task> # machine-readable output
99
+ ```
100
+
101
+ ### Delegation patterns (built into the model)
102
+
103
+ The agent delegates to specialists via the `delegate` tool. You do not invoke it directly — you phrase the request and the model decides.
104
+
105
+ Patterns the model uses:
106
+
107
+ ```ts
108
+ // One focused specialist
109
+ delegate { agent: "nara-droid", task: "..." }
110
+
111
+ // Independent units in parallel (max 8 at once, 4 concurrent)
112
+ delegate { tasks: [
113
+ { agent: "nara-search", task: "research X" },
114
+ { agent: "nara-explore", task: "find usages of Y" },
115
+ { agent: "nara-plan", task: "draft plan for Z" }
116
+ ]}
117
+
118
+ // Sequential, {previous} injects the prior step's output
119
+ delegate { chain: [
120
+ { agent: "nara-explore", task: "map the auth flow" },
121
+ { agent: "nara-plan", task: "draft a plan based on {previous}" },
122
+ { agent: "nara-review", task: "review the plan in {previous}" }
123
+ ]}
124
+ ```
125
+
126
+ ---
127
+
128
+ ## Subagents
129
+
130
+ Naraya ships **11 subagents**. Each is a self-contained folder under `assets/agents/`.
131
+
132
+ ### Coordination
133
+
134
+ | Agent | Purpose | When to use |
135
+ |---|---|---|
136
+ | `nara-build` | Implements features, bugfixes, refactors, releases | Top-level orchestrator. Default for "implement X", "fix Y", "refactor Z" |
137
+ | `nara-architect` | System design, ADRs, component decomposition | Greenfield design, scaling decisions, API contract design, trade-off analysis. Read-only — never edits code |
138
+ | `nara-release` | Version sync, verification, deploy safety | Before any production push, mobile store release, or schema migration |
139
+ | `nara-review` | Multi-aspect code review (security, perf, a11y, API, tests) | Pre-merge review, audit, self-review before push. Does not fix |
140
+ | `nara-debug` | 4-phase root-cause debugging | "X is broken", flaky tests, intermittent failures. Stops after 3 failed fix attempts |
141
+
142
+ ### Investigation (read-only)
143
+
144
+ | Agent | Purpose | When to use |
145
+ |---|---|---|
146
+ | `nara-plan` | Implementation plans, never edits code | Pair with `nara-build` for hand-off |
147
+ | `nara-explore` | Fast codebase navigation (haiku) | "Where is X defined?", "who calls Y?", quick map |
148
+ | `nara-search` | Evidence-first research with citations | Library comparison, version migration, external doc lookup |
149
+
150
+ ### Specialist (writes code in their domain)
151
+
152
+ | Agent | Purpose | When to use |
153
+ |---|---|---|
154
+ | `nara-fe` | Frontend UI/UX (React, Vue, Svelte, Angular, CSS, Tailwind, a11y) | UI changes, component design, responsive, accessibility |
155
+ | `nara-droid` | Native Android (Kotlin, Gradle, Compose, Room, Hilt, adb, APK/AAB) | Android-specific tasks, build failures, release |
156
+ | `nara-vision` | Image analysis using mistral-medium-3-5 vision model | Screenshots, diagrams, UI layouts, code snippets, visual content |
157
+
158
+ ### Picking the right agent
159
+
160
+ The model picks based on the **unit of work**, not the surface form. Quick heuristic:
161
+
162
+ - The user said "implement", "fix", "build", "add", "refactor" → `nara-build`
163
+ - The user said "plan", "design", "architect", "decompose" → `nara-architect` (design) or `nara-plan` (task breakdown)
164
+ - The user said "review", "audit", "check this" → `nara-review`
165
+ - The user said "release", "deploy", "ship" → `nara-release`
166
+ - The user said "debug", "broken", "crash", "flaky" → `nara-debug`
167
+ - The user said "find", "where", "who calls" → `nara-explore`
168
+ - The user said "research", "compare", "what is the best", "how does X work in library Y" → `nara-search`
169
+ - The user said something Android-specific → `nara-droid`
170
+ - The user said something UI/frontend-specific → `nara-fe`
171
+ - The user shared an image, screenshot, or needs visual analysis → `nara-vision`
172
+
173
+ If multiple of these apply, the model fans out to parallel subagents.
174
+
175
+ ---
176
+
177
+ ## Skills
178
+
179
+ Naraya bundles a **global skill pool** at `skills/` (~100 skills) that any agent can load on demand via `/skill:<name>` or by reading `skills/<name>/SKILL.md` directly.
180
+
181
+ Highlights:
182
+
183
+ | Skill | What it covers |
184
+ |---|---|
185
+ | `systematic-debugging` | 4-phase root-cause debugging |
186
+ | `test-driven-development` | RED-GREEN-REFACTOR discipline |
187
+ | `writing-plans` | Implementation plans with bite-sized tasks |
188
+ | `web-app-release-workflow` | Ship Dockerized web apps |
189
+ | `security`, `auth-identity`, `compliance-governance` | Security review and compliance |
190
+ | `architecture`, `api-design-patterns`, `distributed-systems` | System design |
191
+ | `mlops/*` | ML/LLM training, serving, fine-tuning, evaluation |
192
+ | `creative/{humanizer, p5js, claude-design}` | Content / UI generation |
193
+ | `android-kotlin`, `compose-patterns`, `gradle-troubleshoot` | Android development |
194
+ | `react-patterns`, `css-patterns` | Frontend |
195
+
196
+ **Skills vs subagents:** a skill is a workflow / lens the agent applies inline. A subagent is a separate context that runs in parallel. Use skills for "do X the right way", subagents for "do X in isolation".
197
+
198
+ ---
199
+
200
+ ## Agent structure
201
+
202
+ Each agent is a folder under `assets/agents/`. Folder name MUST equal the frontmatter `name:` field.
203
+
204
+ ```
205
+ assets/agents/
206
+ ├── nara-build/ # Generalist implementer (top-level)
207
+ │ ├── AGENTS.md # Frontmatter (name/description/tools/model) + system prompt
208
+ │ ├── skills/ # Agent-specific skills, NOT published to global pool
209
+ │ │ └── engineering-patterns/SKILL.md
210
+ │ ├── scripts/ # Helper shell scripts (chmod +x)
211
+ │ │ ├── impact-scan.sh
212
+ │ │ └── scan.sh
213
+ │ ├── references/ # On-demand docs the agent may read into context
214
+ │ │ └── adr-template.md
215
+ │ ├── assets/ # Static resources (templates, data)
216
+ │ └── README.md # per-agent docs
217
+ ├── nara-plan/ # Read-only planner
218
+ ├── nara-explore/ # Fast read-only code navigation (haiku)
219
+ ├── nara-search/ # Evidence-first research
220
+ ├── nara-droid/ # Native Android specialist
221
+ ├── nara-fe/ # Frontend UI/UX specialist
222
+ ├── nara-vision/ # Image analysis (mistral-medium-3-5 vision)
223
+ ├── nara-architect/ # System design & ADRs (read-only)
224
+ ├── nara-release/ # Release safety
225
+ ├── nara-review/ # Multi-aspect code review
226
+ └── nara-debug/ # 4-phase root-cause debugging
227
+ ```
228
+
229
+ ### Conventions
230
+
231
+ - **Folder name = frontmatter `name:`.** The orchestrator warns but does not fail if they diverge.
232
+ - **`AGENTS.md` is the only file the loader reads.** Everything else is opt-in for the agent to `read` into context.
233
+ - **Per-agent `skills/` are NOT published to the global skill pool.** They are scoped to that agent.
234
+ - **Scripts are executable** (`chmod +x`). They can be invoked from the agent's `bash` tool.
235
+ - **References are docs the agent may read.** Large checklists, protocol walkthroughs, template snippets.
236
+
237
+ ### Adding a new agent
238
+
239
+ 1. Create `assets/agents/<your-role>/AGENTS.md` with frontmatter (`name`, `description`, `tools`, `model`) + body.
240
+ 2. Add the four subdirs: `skills/`, `scripts/`, `references/`, `assets/`.
241
+ 3. (Optional) Add a `README.md` documenting the agent.
242
+ 4. Run `npm test` to confirm the seed manifest picks it up.
243
+ 5. (Optional) Add the agent to the roster in `assets/APPEND-SYSTEM.md` so the model knows to delegate to it.
244
+
245
+ ### Editing an existing agent
246
+
247
+ Edit `assets/agents/<role>/AGENTS.md`. The orchestrator hot-reads this on each `delegate` call, so changes take effect on the next invocation. No restart needed.
248
+
249
+ ---
250
+
251
+ ## Extending
252
+
253
+ ### Adding a skill to a specific agent
254
+
255
+ Drop a directory under `assets/agents/<role>/skills/<skill-name>/` with a `SKILL.md` inside:
256
+
257
+ ```
258
+ assets/agents/nara-fe/
259
+ └── skills/
260
+ └── vue-patterns/
261
+ └── SKILL.md
262
+ ```
263
+
264
+ ### Adding a skill to the global pool
265
+
266
+ Drop it under `skills/<skill-name>/SKILL.md` at the repo root. The global pool is auto-discovered by pi.
267
+
268
+ ### Overriding model per-agent
269
+
270
+ Edit the frontmatter `model:` field in `AGENTS.md`. Or set a per-agent override at runtime with `/agent-model <agent> <model>`.
271
+
272
+ ### Customizing the system prompt
273
+
274
+ Edit `assets/APPEND-SYSTEM.md`. This file is appended to the model's system prompt on every launch.
275
+
276
+ ---
277
+
278
+ ## Development
279
+
280
+ ```bash
281
+ git clone https://gitlab.naraya.ai/adearman/naracli
282
+ cd naracli
283
+ npm install
284
+ npm test # run node:test suite (8 tests)
285
+ ```
286
+
287
+ ### Project layout
288
+
289
+ ```
290
+ naraya-cli/
291
+ ├── bin/
292
+ │ └── naraya.mjs # CLI entry: subcommand dispatch + pi launch
293
+ ├── src/
294
+ │ ├── banner.mjs # login/launch banner
295
+ │ ├── clipboard.mjs # copy auth URL to clipboard
296
+ │ ├── config.mjs # models.json read/write
297
+ │ ├── goodbye.mjs # farewell on quit
298
+ │ ├── login.mjs # naraya login (OAuth-style)
299
+ │ ├── seed.mjs # asset sync to ~/.naraya/agent
300
+ │ ├── splash.mjs # startup animation
301
+ │ └── status.mjs # naraya status (quota, cost)
302
+ ├── assets/
303
+ │ ├── APPEND-SYSTEM.md # injected into system prompt
304
+ │ ├── agents/ # 10 subagent folders (see above)
305
+ │ ├── extensions/ # .ts extensions loaded by pi
306
+ │ ├── skills/ # empty (skills live at repo root for global pool)
307
+ │ └── themes/naraya.json # default theme
308
+ ├── skills/ # global skill pool (~100 skills)
309
+ ├── test/
310
+ │ ├── config.test.mjs
311
+ │ ├── seed.test.mjs # covers agent folder + nested resources
312
+ │ └── status.test.mjs
313
+ ├── package.json
314
+ └── README.md
315
+ ```
316
+
317
+ ### Testing
318
+
319
+ ```bash
320
+ npm test # all 8 tests
321
+ node --test test/seed.test.mjs # just seed (asset sync)
322
+ node --test test/status.test.mjs # just status rendering
323
+ ```
324
+
325
+ The seed test covers both the legacy flat layout and the new folder-based agent layout (AGENTS.md + nested skills/scripts).
326
+
327
+ ---
328
+
329
+ ## Architecture
330
+
331
+ ### Request flow
332
+
333
+ ```
334
+ User input
335
+
336
+
337
+ ┌─────────────────────────┐
338
+ │ bin/naraya.mjs │ CLI entry, subcommand dispatch
339
+ │ - login/logout/status │
340
+ │ - else: launch pi │
341
+ └──────────┬──────────────┘
342
+
343
+
344
+ ┌─────────────────────────┐
345
+ │ src/seed.mjs │ Sync assets/ to ~/.naraya/agent
346
+ │ - assets/agents/* │ (only managed files; user files preserved)
347
+ │ - assets/extensions/* │
348
+ │ - assets/APPEND-SYSTEM │
349
+ └──────────┬──────────────┘
350
+
351
+
352
+ ┌─────────────────────────┐
353
+ │ pi-coding-agent │ pi runtime
354
+ │ - system prompt: │ ← APPEND-SYSTEM.md appended
355
+ │ - skill discovery │ ← global pool + per-agent
356
+ │ - extension loading │ ← naraya-orchestrator, naraya-debug, etc.
357
+ │ - delegate tool │ ← fans out to subagents
358
+ └──────────┬──────────────┘
359
+
360
+
361
+ ┌─────────────────────────┐
362
+ │ Subagent (isolated ctx) │ one per `delegate` call
363
+ │ - reads AGENTS.md │
364
+ │ - reads per-agent │
365
+ │ skills/scripts/refs │
366
+ │ - reads global skills │
367
+ │ - returns result │
368
+ └─────────────────────────┘
369
+
370
+
371
+ Output to user
372
+ ```
373
+
374
+ ### Why folder-based agents?
375
+
376
+ - **Colocation.** Each agent is one self-contained directory: definition + skills + scripts + references + assets. Move it, version it, delete it as one unit.
377
+ - **No global namespace pollution.** Per-agent `skills/` are scoped; only `AGENTS.md` is published.
378
+ - **Easier to share.** A folder is a package. An `.md` file in a flat dir is a file in a list.
379
+ - **Scales.** Adding an agent = adding a folder. Adding an agent to the old flat layout = adding a file that could collide.
380
+ - **Backward compatible.** The orchestrator loader supports both folder layout (`<role>/AGENTS.md`) and legacy flat (`<role>.md`).
381
+
382
+ ### Why split coordination / specialist / investigation?
383
+
384
+ - **Coordination agents** (`nara-build`, `nara-architect`, `nara-release`, `nara-review`, `nara-debug`) need broad context. They route, sequence, verify.
385
+ - **Specialist agents** (`nara-fe`, `nara-droid`) need deep domain knowledge. Their system prompt is shaped by their domain.
386
+ - **Investigation agents** (`nara-plan`, `nara-explore`, `nara-search`) are read-only. They never modify state. They produce artifacts (plans, maps, evidence) that the orchestrator consumes.
387
+
388
+ Mixing these roles in one agent produces shallow specialists and shallow coordinators.
389
+
390
+ ---
391
+
392
+ ## License
393
+
394
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import D from"node:os";import p from"node:path";import f from"node:fs";import"node:crypto";import{AuthStorage as te,ModelRegistry as ne,SessionManager as se,createAgentSession as ae,initTheme as q,getMarkdownTheme as re,getSelectListTheme as oe}from"@earendil-works/pi-coding-agent";import{fileURLToPath as ie}from"node:url";import{TUI as ce,Container as b,Editor as Be,Text as x,Markdown as _e,ProcessTerminal as le,KeybindingsManager as de,TUI_KEYBINDINGS as me,setKeybindings as $e}from"@earendil-works/pi-tui";import{readClipboardImage as ue}from"../src/clipboard.mjs";import"../src/splash.mjs";const F=p.join(D.homedir(),".naraya","native-debug.log"),$=e=>{try{f.appendFileSync(F,`${new Date().toISOString()} ${e}
3
+ `)}catch{}};try{f.rmSync(F,{force:!0})}catch{}process.on("uncaughtException",e=>{$(`UNCAUGHT ${e?.stack??e}`)}),process.on("unhandledRejection",e=>{$(`UNHANDLED ${e?.stack??e}`)});const c="\x1B[38;2;45;123;255m",pe="\x1B[48;2;45;123;255m",he="\x1B[38;2;10;29;77m",ge="\x1B[48;2;10;29;77m",T="\x1B[38;2;255;140;40m",Ne="\x1B[38;2;111;191;115m",B="\x1B[2m",k="\x1B[1m",s="\x1B[0m",fe=/\x1b\[[0-9;]*m/g,m=e=>e.replace(fe,"").length,Ue=(()=>{try{const e=p.dirname(p.dirname(ie(import.meta.url)));return JSON.parse(f.readFileSync(p.join(e,"package.json"),"utf8")).version??"dev"}catch{return"dev"}})(),w=["_____________BB___","___BBB_______BBB__","_BBBBBB______BBBBB","_BBBBBBBB____BBBBB","B_BBBBBBBB___BBBBB","BB_BBBBBBBB__BBBNN","BBB_BBBBBBB__BBNNN","BBBB__BBB____BNNNN","BBBBBB_____BBBNNNN","BBBBBB___BBBBBNNNN","BBBBB___BBBBBBNNNN","BBBBB___BBBBBBNNNN","BBBBB____BBBBBNNNN","BBBBB______BBBNNN_","BBBBB_______BBNN__","_BBBB________B____","__BBB_____________","____B_____________"],M=e=>e==="B"?c:e==="N"?he:"",ye=e=>e==="B"?pe:e==="N"?ge:"";function Ye(){const e=[];for(let t=0;t<w.length;t+=2){let n="";for(let r=0;r<w[t].length;r++){const a=w[t][r],o=w[t+1]?.[r]??"_";a==="_"&&o==="_"?n+=" ":a!=="_"&&o==="_"?n+=`${M(a)}\u2580${s}`:a==="_"&&o!=="_"?n+=`${M(o)}\u2584${s}`:a===o?n+=`${M(a)}\u2588${s}`:n+=`${M(a)}${ye(o)}\u2580${s}`}e.push(n)}return e}const j=(e,t)=>e+" ".repeat(Math.max(0,t-m(e)));function Ke(e,t){const n=Math.max(...e.map(m),0),r=Math.max(...t.map(m),0),a=Math.max(e.length,t.length),o=Math.floor((a-e.length)/2),_=Math.floor((a-t.length)/2);return Array.from({length:a},(N,l)=>`${j(e[l-o]??"",n)} ${j(t[l-_]??"",r)}`)}const E=p.join(D.homedir(),".naraya","agent"),G=p.join(E,"models.json");let U;try{U=JSON.parse(f.readFileSync(G,"utf8"))}catch{console.error("Run `naraya login` first."),process.exit(1)}const y=U.providers?.naraya??{},be=(y.models??[]).map(e=>e.id),h=process.env.NARAYA_MODEL??be[0],xe=(y.models??[]).find(e=>e.id===h)?.name??h;h||(console.error("No naraya models \u2014 run `naraya login`."),process.exit(1));const Y=te.create(p.join(E,"auth.json")),K=ne.create(Y,G),P=K.find("naraya",h);P||(console.error(`Model naraya/${h} not found.`),process.exit(1));const{session:I}=await ae({cwd:process.cwd(),agentDir:E,model:P,thinkingLevel:"off",authStorage:Y,modelRegistry:K,tools:["read","grep","find","ls","edit","write","bash"],sessionManager:se.create(process.cwd())});try{$e(new de(me))}catch{}try{q("naraya",!0)}catch{try{q("dark",!0)}catch{}}const we=re(),Me={borderColor:e=>`${c}${e}${s}`,selectList:oe()};let V=!1,J=!1,z=0,H=0,u=null;async function Se(){try{const e=await fetch(`${y.baseUrl}/me`,{headers:{authorization:`Bearer ${y.apiKey}`},signal:AbortSignal.timeout(3e3)});if(!e.ok)return;const t=await e.json(),n=Number(t.quota?.limit??0),r=Number(t.quota?.remaining??0),a=o=>o>=1e6?`${(o/1e6).toFixed(1).replace(/\.0$/,"")}M`:Number(o).toLocaleString("id-ID");u={email:t.account?.email??"",plan:t.account?.plan??"",saldo:`Rp ${Number(t.credit?.available??0).toLocaleString("id-ID",{maximumFractionDigits:0})}`,usd:t.credit?.usd_equivalent?`$${t.credit.usd_equivalent}`:"",kuota:n>0?`${a(r)} / ${a(n)}`:"fair-use",models:Array.isArray(t.models)?t.models.length:0}}catch{}}const Ae={render(e){const t=Math.min(48,Math.max(40,e-2)),n=t-4,r=(d,L)=>{const ee=Math.max(1,n-m(d)-m(L));return`${d}${" ".repeat(ee)}${L}`},a=y?`${B} v${process.env.NARAYA_VERSION??"dev"}${s}${c}`:"",o=` ${k}NARAYA${s}${a}${c} `,_=`${c}\u256D\u2500${o}${"\u2500".repeat(Math.max(0,t-m(o)-3))}\u256E${s}`,N=`${c}\u2570${"\u2500".repeat(Math.max(0,t-2))}\u256F${s}`,l=d=>`${c}\u2502${s} ${d}${" ".repeat(Math.max(0,n-m(d)))} ${c}\u2502${s}`;return u?[_,l(r(`${k}${u.email}${s}`,`${B}${u.plan}${s}`)),l(r(`${c}Saldo${s} ${u.saldo}`,`${B}${u.usd}${s}`)),l(r(`${c}Kuota${s} ${u.kuota}`,`${B}${u.models} model${s}`)),N,""]:[_,l(`${B}memuat akun\u2026${s}`),N,""]}},Ce={render(e){const t=process.cwd(),n=`${c}\u25C6 ${xe}${s}`,r=Math.max(1,e-m(t)-m(n)),a=`${B}${t}${s}${" ".repeat(r)}${n}`,o=d=>d<1e3?`${d}`:`${(d/1e3).toFixed(1).replace(/\.0$/,"")}k`,_=`${B}\u2191${o(z)} \u2193${o(H)} tokens${s}`,N=`${V?T:B}\u26A1 auto-approve ${V?"ON":"OFF"} \xB7 Ctrl+Alt+A${s}`,l=`${J?T:B}\u2702\uFE0F truncate ${J?"ON":"OFF"} \xB7 Ctrl+Alt+O${s}`;return["\u2500".repeat(e),a,_,N,l]}},i=new ce(new le,!1),W=()=>{try{i.stop()}catch{}try{I.dispose?.()}catch{}process.exit(0)};process.on("SIGINT",W);const X=new b,g=new b,Q=new b,Z=new b;X.addChild(Ae),Z.addChild(Ce);let R=null,O="",S=!1;const A=new Map;let C=0;async function Te(e){$(`submit text=${JSON.stringify((e??"").slice(0,40))} busy=${S}`);let t=(e??"").trim();if(!t&&A.size===0||S){$("submit skipped (empty/busy)");return}const n=[];t=t.replace(/\[🖼 Image #(\d+)\]/g,(a,o)=>{const _=A.get(Number(o));if(_)try{n.push({type:"image",data:f.readFileSync(_.path).toString("base64"),mimeType:_.mimeType})}catch{}return""}).replace(/\s{2,}/g," ").trim(),A.clear(),C=0;const r=n.length?`${B}\u{1F4CE} ${n.length} gambar${s} `:"";g.addChild(new x(`${r}${k}\u203A${s} ${t||"(gambar)"}`,1,0)),O="",R=new _e("",1,0,we),g.addChild(R),i.requestRender(),S=!0;try{await I.prompt(t||"Describe this image.",n.length?{images:n}:void 0)}catch(a){$(`prompt error ${a?.stack??a}`),g.addChild(new x(`${T}[error]${s} ${a?.message??a}`,1,0))}finally{S=!1,i.requestRender()}}class ke extends Be{handleInput(t){if($(`handleInput ${JSON.stringify(t.slice(0,10))} len=${t.length}`),t===""){W();return}if(t==="\r"||t===`
4
+ `){const n=this.getText();this.setText(""),Te(n);return}if(t==="\x1Bv"||t==="\x1BV"){const n=ue();if(n){C+=1,A.set(C,n);const r=this.getText(),a=r&&!r.endsWith(" ")?" ":"";this.setText(`${r}${a}[\u{1F5BC} Image #${C}] `),i.requestRender()}return}super.handleInput(t)}}const v=new ke(i,Me,{placeholder:"Pesan ke Naraya \u2014 Enter kirim \xB7 Alt+V tempel gambar \xB7 Ctrl+C keluar"});Q.addChild(v),I.subscribe(e=>{if($(`event ${e.type}${e.assistantMessageEvent?.type?"/"+e.assistantMessageEvent.type:""}`),e.type==="message_update"&&e.assistantMessageEvent?.type==="text_delta")O+=e.assistantMessageEvent.delta,R?.setText(O),i.requestRender();else if(e.type==="tool_execution_start")g.addChild(new x(`${B}\u2699 ${e.toolName??"tool"}\u2026${s}`,1,0)),i.requestRender();else if(e.type==="turn_end"){const t=e.message?.usage;t&&(z+=t.input??0,H+=t.output??0),i.requestRender()}}),i.addChild(X),i.addChild(g),i.addChild(Q),i.addChild(Z),i.setFocus(v),i.start(),$(`started; focus=${i.focusedComponent===v?"editor":"other"} isTTY=${process.stdin.isTTY}`),g.addChild(new x(`${Ne}\u2713${s} Naraya native \u2014 naraya/${h}. Ketik pesan, Enter kirim.`,1,0)),Se().then(()=>i.requestRender());