gru-ai 0.3.0 → 0.3.1
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 +112 -101
- package/dist-cli/commands/scaffold.d.ts +3 -0
- package/dist-cli/commands/scaffold.js +156 -50
- package/dist-cli/commands/start.js +3 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,18 +11,16 @@
|
|
|
11
11
|
|
|
12
12
|
<p align="center">
|
|
13
13
|
<a href="#what-is-gruai">What Is gruAI?</a> •
|
|
14
|
+
<a href="#quickstart">Quickstart</a> •
|
|
14
15
|
<a href="#the-pipeline">The Pipeline</a> •
|
|
15
|
-
<a href="#the-context-tree">Context Tree</a> •
|
|
16
|
-
<a href="#why-is-the-output-better">Why It Works</a> •
|
|
17
16
|
<a href="#your-team">Your Team</a> •
|
|
18
|
-
<a href="#
|
|
17
|
+
<a href="#why-is-the-output-better">Why It Works</a> •
|
|
18
|
+
<a href="#the-context-tree">Context Tree</a>
|
|
19
19
|
</p>
|
|
20
20
|
|
|
21
|
-
<
|
|
22
|
-
<video src="
|
|
23
|
-
|
|
24
|
-
</video>
|
|
25
|
-
</p>
|
|
21
|
+
<div align="center">
|
|
22
|
+
<video src="https://github.com/user-attachments/assets/8859bc1f-8a04-47ac-9f94-35b9c71741eb" width="720" controls></video>
|
|
23
|
+
</div>
|
|
26
24
|
|
|
27
25
|
---
|
|
28
26
|
|
|
@@ -36,131 +34,87 @@ The system is designed for **depth, not speed.** Agents accumulate institutional
|
|
|
36
34
|
|
|
37
35
|
**You make decisions. Agents make software.** Every directive flows through a 15-step pipeline — triage, audit, brainstorm, plan, build, review, and ship — grounded in published research from Anthropic and OpenAI on what actually makes AI output reliable.
|
|
38
36
|
|
|
39
|
-
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Is gruAI Right for You?
|
|
40
40
|
|
|
41
|
-
✅ You
|
|
42
|
-
✅ You
|
|
43
|
-
✅
|
|
44
|
-
✅ You
|
|
45
|
-
✅ You
|
|
41
|
+
- ✅ You're running 10+ terminals, juggling context, reprompting the same mistakes — and you want to hand down a directive and walk away
|
|
42
|
+
- ✅ You've been burned by agents that "review" their own code — you want reviews that are mandatory, mechanical, and impossible to skip
|
|
43
|
+
- ✅ Your agents forget everything between sessions — you want a team that remembers what broke last time
|
|
44
|
+
- ✅ You're the bottleneck for every decision, every prompt, every context refresh — you want to be the CEO, not the project manager
|
|
45
|
+
- ✅ You like running a one-person company with a full AI team, and you want it to actually feel that way — not like managing a chatbot farm
|
|
46
|
+
- ✅ You want agents that push back on your ideas before building, not agents that say yes and ship the wrong thing
|
|
46
47
|
|
|
47
48
|
---
|
|
48
49
|
|
|
49
50
|
## The Pipeline
|
|
50
51
|
|
|
51
|
-
You
|
|
52
|
+
You type: `/directive Create a landing page for gruAI`. Here's what happens next.
|
|
52
53
|
|
|
53
|
-
|
|
54
|
-
|:----:|---------|
|
|
55
|
-
| :gear: | **System step** — automated, no agent or human involved |
|
|
56
|
-
| :busts_in_silhouette: | **Agent step** — one or more AI agents do the work |
|
|
57
|
-
| :diamond_shape_with_a_dot_inside: | **CEO gate** — pipeline pauses for your decision |
|
|
54
|
+
**1. Triage** — *Automated*
|
|
58
55
|
|
|
59
|
-
|
|
56
|
+
Classified **heavyweight** — touches copy, layout, SEO, and design across multiple files. Each agent gets [role-scoped context](https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents): the CTO gets your tech stack and component patterns, the CMO gets your positioning docs, engineers get your existing layout system. Not a 200K-token dump. [Start simple, add complexity only when needed.](https://www.anthropic.com/research/building-effective-agents)
|
|
60
57
|
|
|
61
|
-
|
|
62
|
-
|:-:|------|:---:|-------------|
|
|
63
|
-
| 1 | :gear: **Triage** | System | Classifies your directive by weight: lightweight, medium, heavyweight, or strategic. *"Add a payment system"* spans API, database, and UI — classified **heavyweight**. [Start simple, add complexity only when needed.](https://www.anthropic.com/research/building-effective-agents) |
|
|
64
|
-
| 2 | :gear: **Checkpoint** | System | Checks for prior progress. If a session died mid-execution, it reads `directive.json` and [resumes from the last completed step](https://www.anthropic.com/engineering/building-c-compiler) — no work is lost. |
|
|
65
|
-
| 3 | :gear: **Read** | System | Parses your directive brief, creates structured metadata, and extracts your Definition of Done. |
|
|
58
|
+
**2. Audit** — *QA Engineer → CTO*
|
|
66
59
|
|
|
67
|
-
|
|
60
|
+
QA scans the codebase: finds the existing `/app` route structure, the Tailwind config, that there's no `og:image` setup, and that the current root page is a placeholder. CTO recommends: reuse the existing layout components, add Open Graph meta from the start, keep it server-rendered for SEO — no need for a SPA or a CMS.
|
|
68
61
|
|
|
69
|
-
|
|
70
|
-
|:-:|------|:---:|-------------|
|
|
71
|
-
| 4 | :gear: **Context** | System | Loads lessons, design docs, and intel — [scoped to what this directive needs](https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents), not a 200K-token dump. |
|
|
72
|
-
| 5 | :busts_in_silhouette: **Audit** | QA Engineer, then CTO | Two-agent sequential audit. QA scans the codebase (pure facts: which files, what state, what breaks). Then the CTO recommends approaches. Identifies existing API patterns, database schema, and security considerations. |
|
|
73
|
-
| 6 | :busts_in_silhouette: **Brainstorm** | CTO + CPO + CMO | C-suite agents independently propose approaches, then [deliberate and argue](https://www.anthropic.com/engineering/multi-agent-research-system). CTO pushes for Stripe, CPO argues for simpler in-house billing, CMO flags pricing page implications. They surface 3 questions for you. |
|
|
62
|
+
**3. Debate** — *CTO + CPO + CMO*
|
|
74
63
|
|
|
75
|
-
|
|
64
|
+
**You're not in the room.** The C-suite agents independently propose approaches, then [argue](https://www.anthropic.com/engineering/multi-agent-research-system):
|
|
76
65
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
66
|
+
> **CTO:** *"Static HTML page. One task, no framework overhead. We don't need React for a landing page — it's marketing content, not an app."*
|
|
67
|
+
>
|
|
68
|
+
> **CPO:** *"A static page won't convert. Developers want to see the product work before they install anything. We need an interactive demo or at minimum an embedded video walkthrough."*
|
|
69
|
+
>
|
|
70
|
+
> **CMO:** *"Neither matters if nobody finds it. An SPA landing page is invisible to Google. Server-rendered with proper meta tags, structured data, and a clear CTA — or we're building for an audience of zero."*
|
|
82
71
|
|
|
83
|
-
|
|
72
|
+
They find common ground: server-rendered page with an embedded demo video. Then they surface 3 questions for you.
|
|
84
73
|
|
|
85
|
-
|
|
86
|
-
|:-:|------|:---:|-------------|
|
|
87
|
-
| 10 | :busts_in_silhouette: **Project Brainstorm** | CTO + assigned builder | Break each project into concrete tasks with Definition of Done criteria. API project gets 3 tasks: Stripe integration, webhook handling, subscription management. |
|
|
88
|
-
| 11 | :gear: **Setup** | System | Creates a git branch to isolate changes. |
|
|
89
|
-
| 12 | :busts_in_silhouette: **Execute** | Assigned builders + reviewers | Builders work through tasks. After each task, a [separate reviewer evaluates with fresh context](https://www.anthropic.com/research/building-effective-agents) — no builder reasoning, no confirmation bias. Failed review triggers a fix cycle. |
|
|
74
|
+
**4. Clarify** — 👤 **You**
|
|
90
75
|
|
|
91
|
-
|
|
76
|
+
You answer 3 questions: *"Should the demo be live or a video?"* → video for now. *"Target audience — developers or technical founders?"* → developers first. *"Ship under /landing or replace the root?"* → replace root, the current placeholder adds no value.
|
|
92
77
|
|
|
93
|
-
|
|
94
|
-
|:-:|------|:---:|-------------|
|
|
95
|
-
| 13 | :gear: **Review Gate** | System | Bash scripts — not LLMs — [mechanically verify](https://openai.com/index/harness-engineering/) that every task was reviewed by a different agent, every DOD criterion was evaluated, and review artifacts exist. |
|
|
96
|
-
| 14 | :gear: **Wrapup** | System | Updates lessons and design docs. Generates a CEO digest with files changed, review results, and revert commands. [Knowledge persists](https://arxiv.org/abs/2602.20478) for future directives. |
|
|
97
|
-
| 15 | :diamond_shape_with_a_dot_inside: **Completion** | **CEO** | **Mandatory for all weights.** You review the digest and decide: approve (ship it), amend (fix specific issues), or reopen (start over). The pipeline never ships without your sign-off. |
|
|
78
|
+
**5. Plan** — *COO* → 👤 **You**
|
|
98
79
|
|
|
99
|
-
|
|
80
|
+
COO breaks it into 2 projects with 6 tasks:
|
|
81
|
+
- **Project 1:** Full-stack engineer builds page structure — hero with tagline and demo video, feature grid with pipeline highlights, agent team section, and Open Graph meta. 4 tasks.
|
|
82
|
+
- **Project 2:** Frontend engineer builds responsive layout — mobile breakpoints, CTA positioning, and dark mode support. 2 tasks.
|
|
100
83
|
|
|
101
|
-
|
|
102
|
-
|--------|---------|-------|-----------|
|
|
103
|
-
| **Lightweight** | Fix a typo | Brainstorm | Completion only |
|
|
104
|
-
| **Medium** | Add dark mode toggle | Brainstorm | Completion only |
|
|
105
|
-
| **Heavyweight** | New payment system | Nothing | Clarification + Approve + Completion |
|
|
106
|
-
| **Strategic** | Platform migration | Nothing | Clarification + Approve + Completion |
|
|
84
|
+
CTO reviews both projects. Each task has a Definition of Done: *"Hero section renders demo video with fallback image, loads in under 2s on 3G."* **You approve the plan** before any code is written.
|
|
107
85
|
|
|
108
|
-
|
|
86
|
+
**6. Build** — *Engineers*
|
|
109
87
|
|
|
110
|
-
|
|
88
|
+
Full-stack engineer builds the hero section, feature grid, and OG meta on an isolated git branch. Each task runs in a [clean context window](https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents) — the agent building the hero section doesn't carry leftover context from the meta tags task. After each task, a [separate reviewer evaluates with fresh context](https://www.anthropic.com/research/building-effective-agents) — no builder reasoning, no confirmation bias.
|
|
111
89
|
|
|
112
|
-
|
|
90
|
+
**7. Review** — *Reviewers + Automated*
|
|
113
91
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
│ └── dark-mode/
|
|
118
|
-
│ ├── directive.json # Pipeline state, weight, progress
|
|
119
|
-
│ ├── directive.md # CEO brief
|
|
120
|
-
│ ├── audit.md # CTO's technical audit
|
|
121
|
-
│ ├── brainstorm.md # C-suite deliberation
|
|
122
|
-
│ └── projects/
|
|
123
|
-
│ └── dark-mode/
|
|
124
|
-
│ └── project.json # Tasks, DOD, agents, reviews
|
|
125
|
-
├── lessons/ # What went wrong (reactive)
|
|
126
|
-
├── design/ # Why the system works this way (proactive)
|
|
127
|
-
├── intel/ # External research from /scout
|
|
128
|
-
└── reports/ # CEO digests
|
|
129
|
-
```
|
|
92
|
+
> **Round 1:** CTO reviews the hero section. Finds 3 issues: missing `<meta description>` tag (SEO blind spot), layout breaks below 375px on the feature grid (iPhone SE), and no `loading="lazy"` on the demo video (kills page speed score). Sends findings back to builder with specific file locations and expected fixes.
|
|
93
|
+
>
|
|
94
|
+
> **Round 2:** Builder fixes all three. CTO reviews again with fresh context — doesn't remember giving the feedback, evaluates the code on its own merit. Finds one remaining issue: `#aaa` text on `#fff` background in the feature section fails WCAG AA contrast (4.5:1 required, got 2.7:1). Builder bumps to `#595959`. **Passes.**
|
|
130
95
|
|
|
131
|
-
|
|
96
|
+
Bash scripts [verify](https://openai.com/index/harness-engineering/) every task was reviewed by a different agent than the one that built it. No review can be skipped, faked, or self-certified. This [evaluator-optimizer loop](https://www.anthropic.com/research/building-effective-agents) runs up to 3 rounds per task.
|
|
132
97
|
|
|
133
|
-
**
|
|
98
|
+
**8. Ship** — *Automated*
|
|
134
99
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
## Why Is the Output Better?
|
|
138
|
-
|
|
139
|
-
Every point below traces to published research from Anthropic and OpenAI. This isn't a workflow we invented — it's assembled from what the research says actually works.
|
|
140
|
-
|
|
141
|
-
- **Agents brainstorm and argue before anyone writes code.** For strategic directives, your C-suite agents independently propose approaches, then deliberate — challenging assumptions, resolving disagreements, and surfacing questions for you. Anthropic's research found [multi-agent outperformed single-agent by 90.2%](https://www.anthropic.com/engineering/multi-agent-research-system). The pipeline implements their [orchestrator-workers pattern](https://www.anthropic.com/research/building-effective-agents) where specialized agents collaborate, producing better results than any single agent.
|
|
100
|
+
Lessons captured: *"Always add OG meta tags when creating new pages"* and *"Check WCAG contrast on all text colors."* Design docs updated with the new route structure. CEO digest generated with all 8 files changed, both review rounds summarized, and `git revert` commands ready if needed. [Knowledge persists](https://arxiv.org/abs/2602.20478) — next time someone creates a page, agents already know about the OG tags.
|
|
142
101
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
- **Context is isolated, not accumulated.** Each agent spawns with a [clean context window](https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents) scoped to exactly what it needs. No 200K-token sessions where the model forgets what it read at the start. Anthropic's context engineering research shows accuracy degrades as token count increases — gruAI treats context as a finite resource under active degradation.
|
|
102
|
+
**9. Your Call** — 👤 **You**
|
|
146
103
|
|
|
147
|
-
|
|
104
|
+
You see the digest: 8 files changed, 2 review rounds, all 6 DOD criteria met, zero open issues. You open the page in your browser, check mobile, and decide: **approve** it as-is, **amend** with specific fixes (*"bump the CTA font size"*), **extend** the directive with new scope (*"now add a pricing section"*), or **redirect** if the approach was wrong. The pipeline never ships without your sign-off — and never limits you to just "accept" or "reject."
|
|
148
105
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
- **Memory compounds across directives.** Lessons, design rationale, and standing corrections persist in `.context/` and get loaded into every future agent. This implements the [codified context pattern](https://arxiv.org/abs/2602.20478) — hot-memory + specialized agents + cold-memory knowledge base.
|
|
106
|
+
> *The pipeline runs 15 internal steps. Context loading, crash recovery, git isolation, and lesson extraction run automatically between the steps above.*
|
|
152
107
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
## Why Not Just Use Claude Code Directly?
|
|
156
|
-
|
|
157
|
-
Claude Code is the execution engine. gruAI is the management layer on top.
|
|
158
|
-
|
|
159
|
-
Without gruAI, you prompt an agent, review the output, re-prompt to fix issues, manage context yourself, and hope nothing falls through the cracks. There is no review gate, no institutional memory, and no structure beyond what you hold in your head.
|
|
108
|
+
### Weight Adaptation
|
|
160
109
|
|
|
161
|
-
|
|
110
|
+
Not every directive needs the full process.
|
|
162
111
|
|
|
163
|
-
|
|
112
|
+
| Weight | Example | Skips | You Decide At |
|
|
113
|
+
|--------|---------|-------|---------------|
|
|
114
|
+
| **Lightweight** | Fix a typo | Debate | Accept only |
|
|
115
|
+
| **Medium** | Add dark mode | Debate | Accept only |
|
|
116
|
+
| **Heavyweight** | New landing page | Nothing | Clarify + Plan + Accept |
|
|
117
|
+
| **Strategic** | Platform migration | Nothing | Clarify + Plan + Accept |
|
|
164
118
|
|
|
165
119
|
---
|
|
166
120
|
|
|
@@ -248,6 +202,63 @@ The pipeline and dashboard are engine-agnostic by design — platform adapters h
|
|
|
248
202
|
|
|
249
203
|
---
|
|
250
204
|
|
|
205
|
+
## Why Is the Output Better?
|
|
206
|
+
|
|
207
|
+
Every point below traces to published research from Anthropic and OpenAI. This isn't a workflow we invented — it's assembled from what the research says actually works.
|
|
208
|
+
|
|
209
|
+
- **Agents brainstorm and argue before anyone writes code.** For strategic directives, your C-suite agents independently propose approaches, then deliberate — challenging assumptions, resolving disagreements, and surfacing questions for you. Anthropic's research found [multi-agent outperformed single-agent by 90.2%](https://www.anthropic.com/engineering/multi-agent-research-system). The pipeline implements their [orchestrator-workers pattern](https://www.anthropic.com/research/building-effective-agents) where specialized agents collaborate, producing better results than any single agent.
|
|
210
|
+
|
|
211
|
+
- **Reviewers evaluate intent, not just code.** Each reviewer gets [fresh context](https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents) scoped to the task — they never see the builder's reasoning, preventing confirmation bias. They verify against your Definition of Done (what you asked for), not just whether the code compiles. This is Anthropic's [evaluator-optimizer pattern](https://www.anthropic.com/research/building-effective-agents): one agent generates, another evaluates, issues get fixed in-loop — not after the fact.
|
|
212
|
+
|
|
213
|
+
- **Context is isolated, not accumulated.** Each agent spawns with a [clean context window](https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents) scoped to exactly what it needs. No 200K-token sessions where the model forgets what it read at the start. Anthropic's context engineering research shows accuracy degrades as token count increases — gruAI treats context as a finite resource under active degradation.
|
|
214
|
+
|
|
215
|
+
- **Verification is mechanical.** Bash scripts — not LLMs — enforce pipeline integrity: schema validation, self-review prevention, step dependency checks, role assignment verification. This follows Anthropic's [poka-yoke principle](https://www.anthropic.com/research/building-effective-agents) (error-proof design) and OpenAI's finding that [invariants should be enforced through structural tests](https://openai.com/index/harness-engineering/), not judgment.
|
|
216
|
+
|
|
217
|
+
- **The harness determines output quality, not model intelligence.** Anthropic found that ["the task verifier must be nearly perfect, otherwise the agent solves the wrong problem"](https://www.anthropic.com/engineering/building-c-compiler). OpenAI's team reached the same conclusion: [3 engineers produced 1M lines of code](https://openai.com/index/harness-engineering/) not by writing better prompts, but by designing better environments and feedback loops. gruAI's 15-step pipeline IS that harness.
|
|
218
|
+
|
|
219
|
+
- **Memory compounds across directives.** Lessons, design rationale, and standing corrections persist in `.context/` and get loaded into every future agent. This implements the [codified context pattern](https://arxiv.org/abs/2602.20478) — hot-memory + specialized agents + cold-memory knowledge base.
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Why Not Just Use Claude Code Directly?
|
|
224
|
+
|
|
225
|
+
Claude Code is the execution engine. gruAI is the management layer on top.
|
|
226
|
+
|
|
227
|
+
Without gruAI, you prompt an agent, review the output, re-prompt to fix issues, manage context yourself, and hope nothing falls through the cracks. There is no review gate, no institutional memory, and no structure beyond what you hold in your head.
|
|
228
|
+
|
|
229
|
+
With gruAI, you hand down a directive and agents self-organize through a 15-step pipeline. Reviews are mandatory and mechanical — a different agent reviews each task, and bash scripts verify the reviews actually happened. Lessons persist across directives so your 10th task runs better than your 1st. You approve the result, not every step along the way.
|
|
230
|
+
|
|
231
|
+
gruAI doesn't replace Claude Code. It makes Claude Code work like a team instead of a solo assistant.
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## The Context Tree
|
|
236
|
+
|
|
237
|
+
All state lives in `.context/` at your repo root — plain markdown and JSON, version-controlled alongside your code.
|
|
238
|
+
|
|
239
|
+
```
|
|
240
|
+
.context/
|
|
241
|
+
├── directives/ # All work lives here
|
|
242
|
+
│ └── dark-mode/
|
|
243
|
+
│ ├── directive.json # Pipeline state, weight, progress
|
|
244
|
+
│ ├── directive.md # CEO brief
|
|
245
|
+
│ ├── audit.md # CTO's technical audit
|
|
246
|
+
│ ├── brainstorm.md # C-suite deliberation
|
|
247
|
+
│ └── projects/
|
|
248
|
+
│ └── dark-mode/
|
|
249
|
+
│ └── project.json # Tasks, DOD, agents, reviews
|
|
250
|
+
├── lessons/ # What went wrong (reactive)
|
|
251
|
+
├── design/ # Why the system works this way (proactive)
|
|
252
|
+
├── intel/ # External research from /scout
|
|
253
|
+
└── reports/ # CEO digests
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
**Directive → Projects → Tasks.** A directive is a unit of work ("add dark mode"). The COO decomposes it into projects, each with tasks, agents, reviewers, and a Definition of Done. `directive.json` tracks pipeline progress — any session can read it and resume where it left off. `project.json` is the source of truth for what needs building and whether it passed review.
|
|
257
|
+
|
|
258
|
+
**Knowledge compounds.** Lessons, design rationale, and standing corrections persist across directives. Agents load relevant context just-in-time — not everything, just what they need for their role and task. No database, no external service — just files.
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
251
262
|
<details>
|
|
252
263
|
<summary><strong>Terminal Support</strong></summary>
|
|
253
264
|
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Scaffolding engine — creates all project files from templates.
|
|
3
3
|
*
|
|
4
|
+
* SAFE BY DEFAULT: Never overwrites existing user files. Only writes new files.
|
|
5
|
+
* gruai-owned files (agents, skills, registry) are always updated.
|
|
6
|
+
*
|
|
4
7
|
* Takes an InitConfig and produces:
|
|
5
8
|
* - .gruai/ canonical home directory
|
|
6
9
|
* - Platform symlink (.claude/, .aider/, etc.)
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Scaffolding engine — creates all project files from templates.
|
|
3
3
|
*
|
|
4
|
+
* SAFE BY DEFAULT: Never overwrites existing user files. Only writes new files.
|
|
5
|
+
* gruai-owned files (agents, skills, registry) are always updated.
|
|
6
|
+
*
|
|
4
7
|
* Takes an InitConfig and produces:
|
|
5
8
|
* - .gruai/ canonical home directory
|
|
6
9
|
* - Platform symlink (.claude/, .aider/, etc.)
|
|
@@ -25,6 +28,13 @@ function writeFile(filePath, content) {
|
|
|
25
28
|
ensureDir(path.dirname(filePath));
|
|
26
29
|
fs.writeFileSync(filePath, content, 'utf-8');
|
|
27
30
|
}
|
|
31
|
+
/** Write only if file does not exist. Returns true if written, false if skipped. */
|
|
32
|
+
function writeFileIfNew(filePath, content) {
|
|
33
|
+
if (fs.existsSync(filePath))
|
|
34
|
+
return false;
|
|
35
|
+
writeFile(filePath, content);
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
28
38
|
function copyFile(src, dest) {
|
|
29
39
|
ensureDir(path.dirname(dest));
|
|
30
40
|
fs.copyFileSync(src, dest);
|
|
@@ -64,24 +74,39 @@ function platformDir(platform) {
|
|
|
64
74
|
function scaffoldGruaiHome(projectPath, platform) {
|
|
65
75
|
const gruaiDir = path.join(projectPath, '.gruai');
|
|
66
76
|
ensureDir(gruaiDir);
|
|
67
|
-
|
|
77
|
+
const merged = [];
|
|
68
78
|
const platDir = platformDir(platform);
|
|
69
79
|
if (platDir) {
|
|
70
80
|
const platPath = path.join(projectPath, platDir);
|
|
71
|
-
// If platform dir already exists as a real directory, skip symlink
|
|
72
81
|
if (fs.existsSync(platPath)) {
|
|
73
82
|
const stat = fs.lstatSync(platPath);
|
|
74
83
|
if (stat.isSymbolicLink()) {
|
|
84
|
+
// Already a symlink — check if it points to .gruai
|
|
85
|
+
const target = fs.readlinkSync(platPath);
|
|
86
|
+
if (target === '.gruai' || target === path.join(projectPath, '.gruai')) {
|
|
87
|
+
// Already correct, nothing to do
|
|
88
|
+
return { merged };
|
|
89
|
+
}
|
|
75
90
|
fs.unlinkSync(platPath);
|
|
76
91
|
}
|
|
77
92
|
else {
|
|
78
|
-
// Real directory
|
|
79
|
-
const entries = fs.readdirSync(platPath);
|
|
93
|
+
// Real directory — merge contents into .gruai preserving existing files
|
|
94
|
+
const entries = fs.readdirSync(platPath, { withFileTypes: true });
|
|
80
95
|
for (const entry of entries) {
|
|
81
|
-
const src = path.join(platPath, entry);
|
|
82
|
-
const dest = path.join(gruaiDir, entry);
|
|
83
|
-
if (
|
|
96
|
+
const src = path.join(platPath, entry.name);
|
|
97
|
+
const dest = path.join(gruaiDir, entry.name);
|
|
98
|
+
if (fs.existsSync(dest)) {
|
|
99
|
+
// Destination exists — merge directories, skip files
|
|
100
|
+
if (entry.isDirectory() && fs.statSync(dest).isDirectory()) {
|
|
101
|
+
// Merge directory contents (existing files in .gruai win)
|
|
102
|
+
mergeDir(src, dest);
|
|
103
|
+
merged.push(entry.name + '/');
|
|
104
|
+
}
|
|
105
|
+
// Skip files that already exist in .gruai
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
84
108
|
fs.renameSync(src, dest);
|
|
109
|
+
merged.push(entry.name);
|
|
85
110
|
}
|
|
86
111
|
}
|
|
87
112
|
fs.rmSync(platPath, { recursive: true, force: true });
|
|
@@ -90,13 +115,33 @@ function scaffoldGruaiHome(projectPath, platform) {
|
|
|
90
115
|
// Create symlink: .claude -> .gruai
|
|
91
116
|
fs.symlinkSync('.gruai', platPath);
|
|
92
117
|
}
|
|
118
|
+
return { merged };
|
|
119
|
+
}
|
|
120
|
+
/** Recursively merge src into dest, never overwriting existing files in dest. */
|
|
121
|
+
function mergeDir(src, dest) {
|
|
122
|
+
ensureDir(dest);
|
|
123
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
124
|
+
for (const entry of entries) {
|
|
125
|
+
const srcPath = path.join(src, entry.name);
|
|
126
|
+
const destPath = path.join(dest, entry.name);
|
|
127
|
+
if (entry.isDirectory()) {
|
|
128
|
+
mergeDir(srcPath, destPath);
|
|
129
|
+
}
|
|
130
|
+
else if (!fs.existsSync(destPath)) {
|
|
131
|
+
fs.renameSync(srcPath, destPath);
|
|
132
|
+
}
|
|
133
|
+
// If destPath exists, user's file wins — do nothing
|
|
134
|
+
}
|
|
93
135
|
}
|
|
94
136
|
function scaffoldAgents(projectPath, agents) {
|
|
95
137
|
const destDir = path.join(projectPath, '.gruai', 'agents');
|
|
96
|
-
let
|
|
138
|
+
let written = 0;
|
|
139
|
+
const skipped = [];
|
|
97
140
|
for (const agent of agents) {
|
|
98
141
|
const roleId = ROLE_DEFINITIONS.find(r => r.title === agent.title)?.id;
|
|
99
142
|
const templatePath = path.join(ROLE_TEMPLATES_DIR, `${roleId}.md`);
|
|
143
|
+
const destPath = path.join(destDir, agent.agentFile);
|
|
144
|
+
// Agent personality files are gruai-owned — always write
|
|
100
145
|
let content;
|
|
101
146
|
if (fs.existsSync(templatePath)) {
|
|
102
147
|
content = fs.readFileSync(templatePath, 'utf-8');
|
|
@@ -107,10 +152,10 @@ function scaffoldAgents(projectPath, agents) {
|
|
|
107
152
|
else {
|
|
108
153
|
content = `# ${agent.name} -- ${agent.role}\n\nRole template not found.\n`;
|
|
109
154
|
}
|
|
110
|
-
writeFile(
|
|
111
|
-
|
|
155
|
+
writeFile(destPath, content);
|
|
156
|
+
written++;
|
|
112
157
|
}
|
|
113
|
-
return
|
|
158
|
+
return { written, skipped };
|
|
114
159
|
}
|
|
115
160
|
function scaffoldRegistry(projectPath, agents, preset) {
|
|
116
161
|
const ceoEntry = {
|
|
@@ -148,6 +193,7 @@ function scaffoldRegistry(projectPath, agents, preset) {
|
|
|
148
193
|
}));
|
|
149
194
|
const teams = buildTeams(agents);
|
|
150
195
|
const registry = { teamSize: preset, agents: [ceoEntry, ...agentEntries], teams };
|
|
196
|
+
// Registry is gruai-owned — always overwrite
|
|
151
197
|
writeFile(path.join(projectPath, '.gruai', 'agent-registry.json'), JSON.stringify(registry, null, 2));
|
|
152
198
|
}
|
|
153
199
|
function buildTeams(agents) {
|
|
@@ -201,7 +247,8 @@ function buildTeams(agents) {
|
|
|
201
247
|
}
|
|
202
248
|
function scaffoldSkills(projectPath) {
|
|
203
249
|
const destDir = path.join(projectPath, '.gruai', 'skills');
|
|
204
|
-
let
|
|
250
|
+
let written = 0;
|
|
251
|
+
let updated = 0;
|
|
205
252
|
for (const skill of ALL_SKILLS) {
|
|
206
253
|
const skillDir = path.join(SKILLS_SRC_DIR, skill);
|
|
207
254
|
const skillMd = path.join(skillDir, 'SKILL.md');
|
|
@@ -209,28 +256,52 @@ function scaffoldSkills(projectPath) {
|
|
|
209
256
|
console.warn(c.yellow(` Warning: Skill file not found: ${skill}/SKILL.md (skipped)`));
|
|
210
257
|
continue;
|
|
211
258
|
}
|
|
212
|
-
|
|
259
|
+
const destSkillMd = path.join(destDir, skill, 'SKILL.md');
|
|
260
|
+
const existed = fs.existsSync(destSkillMd);
|
|
261
|
+
// Skills are gruai-owned — always update to latest version
|
|
262
|
+
copyFile(skillMd, destSkillMd);
|
|
213
263
|
const docsDir = path.join(skillDir, 'docs');
|
|
214
264
|
if (fs.existsSync(docsDir) && fs.statSync(docsDir).isDirectory()) {
|
|
215
265
|
copyDirRecursive(docsDir, path.join(destDir, skill, 'docs'));
|
|
216
266
|
}
|
|
217
|
-
|
|
267
|
+
if (existed)
|
|
268
|
+
updated++;
|
|
269
|
+
else
|
|
270
|
+
written++;
|
|
218
271
|
}
|
|
219
|
-
return
|
|
272
|
+
return { written, updated };
|
|
220
273
|
}
|
|
221
274
|
function scaffoldContext(projectPath, config) {
|
|
222
275
|
const contextDir = path.join(projectPath, '.context');
|
|
223
|
-
|
|
276
|
+
const created = [];
|
|
277
|
+
const preserved = [];
|
|
278
|
+
// vision.md — user-owned, skip if exists
|
|
279
|
+
const visionPath = path.join(contextDir, 'vision.md');
|
|
224
280
|
const vision = readTemplate('vision.md').replace(/\{\{PROJECT_NAME\}\}/g, config.projectName);
|
|
225
|
-
|
|
226
|
-
|
|
281
|
+
if (writeFileIfNew(visionPath, vision))
|
|
282
|
+
created.push('vision.md');
|
|
283
|
+
else
|
|
284
|
+
preserved.push('vision.md');
|
|
285
|
+
// lessons/index.md — user-owned, skip if exists
|
|
286
|
+
const lessonsPath = path.join(contextDir, 'lessons', 'index.md');
|
|
227
287
|
const lessons = readTemplate('lessons.md').replace(/\{\{PROJECT_NAME\}\}/g, config.projectName);
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
288
|
+
if (writeFileIfNew(lessonsPath, lessons))
|
|
289
|
+
created.push('lessons/index.md');
|
|
290
|
+
else
|
|
291
|
+
preserved.push('lessons/index.md');
|
|
292
|
+
// preferences.md — user-owned, skip if exists
|
|
293
|
+
const prefsPath = path.join(contextDir, 'preferences.md');
|
|
294
|
+
if (writeFileIfNew(prefsPath, `# CEO Preferences\n\n> Standing orders for the team. Agents read this before every task.\n\n## Standing Orders\n\n- (Add your preferences here)\n`))
|
|
295
|
+
created.push('preferences.md');
|
|
296
|
+
else
|
|
297
|
+
preserved.push('preferences.md');
|
|
298
|
+
// backlog.json — user-owned, skip if exists
|
|
299
|
+
const backlogPath = path.join(contextDir, 'backlog.json');
|
|
232
300
|
const backlog = readTemplate('backlog.json.template');
|
|
233
|
-
|
|
301
|
+
if (writeFileIfNew(backlogPath, backlog))
|
|
302
|
+
created.push('backlog.json');
|
|
303
|
+
else
|
|
304
|
+
preserved.push('backlog.json');
|
|
234
305
|
// Empty directories with .gitkeep
|
|
235
306
|
for (const dir of ['directives', 'reports']) {
|
|
236
307
|
const dirPath = path.join(contextDir, dir);
|
|
@@ -240,8 +311,14 @@ function scaffoldContext(projectPath, config) {
|
|
|
240
311
|
fs.writeFileSync(gitkeep, '', 'utf-8');
|
|
241
312
|
}
|
|
242
313
|
}
|
|
314
|
+
return { created, preserved };
|
|
243
315
|
}
|
|
244
316
|
function scaffoldClaudeMd(projectPath, config) {
|
|
317
|
+
const claudeMdPath = path.join(projectPath, 'CLAUDE.md');
|
|
318
|
+
// User-owned file — never overwrite
|
|
319
|
+
if (fs.existsSync(claudeMdPath)) {
|
|
320
|
+
return 'preserved';
|
|
321
|
+
}
|
|
245
322
|
const template = readTemplate('CLAUDE.md.template');
|
|
246
323
|
const rosterLines = ['| Name | Title | Role |', '|------|-------|------|'];
|
|
247
324
|
rosterLines.push('| (You) | CEO | Chief Executive Officer |');
|
|
@@ -251,9 +328,12 @@ function scaffoldClaudeMd(projectPath, config) {
|
|
|
251
328
|
const content = template
|
|
252
329
|
.replace(/\{\{PROJECT_NAME\}\}/g, config.projectName)
|
|
253
330
|
.replace(/\{\{AGENT_ROSTER\}\}/g, rosterLines.join('\n'));
|
|
254
|
-
writeFile(
|
|
331
|
+
writeFile(claudeMdPath, content);
|
|
332
|
+
return 'created';
|
|
255
333
|
}
|
|
256
334
|
function scaffoldGruaiConfig(projectPath, config) {
|
|
335
|
+
const configPath = path.join(projectPath, 'gruai.config.json');
|
|
336
|
+
const existed = fs.existsSync(configPath);
|
|
257
337
|
const template = readTemplate('gruai.config.json.template');
|
|
258
338
|
const agentsJson = JSON.stringify(config.agents.map(a => ({ id: a.id, name: a.name, role: a.title })), null, 4);
|
|
259
339
|
const indentedAgentsJson = agentsJson
|
|
@@ -265,19 +345,25 @@ function scaffoldGruaiConfig(projectPath, config) {
|
|
|
265
345
|
.replace(/\{\{PRESET\}\}/g, config.preset)
|
|
266
346
|
.replace(/\{\{PLATFORM\}\}/g, config.platform)
|
|
267
347
|
.replace(/\{\{AGENTS_JSON\}\}/g, indentedAgentsJson);
|
|
268
|
-
|
|
348
|
+
// gruai config is gruai-owned — always update
|
|
349
|
+
writeFile(configPath, content);
|
|
350
|
+
return existed ? 'updated' : 'created';
|
|
269
351
|
}
|
|
270
352
|
function scaffoldWelcomeDirective(projectPath) {
|
|
271
353
|
const srcDir = path.join(TEMPLATES_DIR, 'welcome-directive');
|
|
272
354
|
if (!fs.existsSync(srcDir))
|
|
273
|
-
return;
|
|
355
|
+
return false;
|
|
274
356
|
const destDir = path.join(projectPath, '.context', 'directives', 'welcome');
|
|
357
|
+
// User-owned content — skip if directive already exists
|
|
358
|
+
if (fs.existsSync(path.join(destDir, 'directive.json')))
|
|
359
|
+
return false;
|
|
275
360
|
ensureDir(destDir);
|
|
276
361
|
// directive.json
|
|
277
362
|
const djson = fs.readFileSync(path.join(srcDir, 'directive.json'), 'utf-8');
|
|
278
363
|
writeFile(path.join(destDir, 'directive.json'), djson.replace(/\{\{CREATED_AT\}\}/g, new Date().toISOString()));
|
|
279
364
|
// directive.md
|
|
280
365
|
copyFile(path.join(srcDir, 'directive.md'), path.join(destDir, 'directive.md'));
|
|
366
|
+
return true;
|
|
281
367
|
}
|
|
282
368
|
// ─── Main scaffold function ─────────────────────────────────────────────────
|
|
283
369
|
export async function runScaffold(config) {
|
|
@@ -287,35 +373,55 @@ export async function runScaffold(config) {
|
|
|
287
373
|
}
|
|
288
374
|
console.log(`\n ${c.bold('Scaffolding gruai...')}\n`);
|
|
289
375
|
// 1. .gruai/ home + platform symlink
|
|
290
|
-
scaffoldGruaiHome(config.projectPath, config.platform);
|
|
376
|
+
const { merged } = scaffoldGruaiHome(config.projectPath, config.platform);
|
|
291
377
|
const platDir = platformDir(config.platform);
|
|
292
378
|
if (platDir) {
|
|
293
379
|
console.log(c.green(` [+] Home: .gruai/ with ${platDir}/ -> .gruai/ symlink`));
|
|
380
|
+
if (merged.length > 0) {
|
|
381
|
+
console.log(c.dim(` merged ${merged.length} existing entries from ${platDir}/`));
|
|
382
|
+
}
|
|
294
383
|
}
|
|
295
384
|
else {
|
|
296
385
|
console.log(c.green(` [+] Home: .gruai/`));
|
|
297
386
|
}
|
|
298
|
-
// 2. Agent registry
|
|
387
|
+
// 2. Agent registry (gruai-owned — always update)
|
|
299
388
|
scaffoldRegistry(config.projectPath, config.agents, config.preset);
|
|
300
|
-
console.log(c.green(` [+] Registry:
|
|
301
|
-
// 3. Agent personality files
|
|
302
|
-
const
|
|
303
|
-
console.log(c.green(` [+] Agents: ${
|
|
304
|
-
// 4. Skills
|
|
305
|
-
const
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
389
|
+
console.log(c.green(` [+] Registry: agent-registry.json (${config.agents.length} agents + CEO, ${config.preset} preset)`));
|
|
390
|
+
// 3. Agent personality files (gruai-owned — always update)
|
|
391
|
+
const agentResult = scaffoldAgents(config.projectPath, config.agents);
|
|
392
|
+
console.log(c.green(` [+] Agents: ${agentResult.written} personality files`));
|
|
393
|
+
// 4. Skills (gruai-owned — always update to latest)
|
|
394
|
+
const skillResult = scaffoldSkills(config.projectPath);
|
|
395
|
+
const skillParts = [];
|
|
396
|
+
if (skillResult.written > 0)
|
|
397
|
+
skillParts.push(`${skillResult.written} new`);
|
|
398
|
+
if (skillResult.updated > 0)
|
|
399
|
+
skillParts.push(`${skillResult.updated} updated`);
|
|
400
|
+
console.log(c.green(` [+] Skills: ${skillParts.join(', ') || 'up to date'}`));
|
|
401
|
+
// 5. Context tree (user-owned — never overwrite)
|
|
402
|
+
const ctxResult = scaffoldContext(config.projectPath, config);
|
|
403
|
+
if (ctxResult.created.length > 0) {
|
|
404
|
+
console.log(c.green(` [+] Context: ${ctxResult.created.join(', ')}`));
|
|
405
|
+
}
|
|
406
|
+
if (ctxResult.preserved.length > 0) {
|
|
407
|
+
console.log(c.dim(` [=] Context: ${ctxResult.preserved.join(', ')} (preserved)`));
|
|
408
|
+
}
|
|
409
|
+
// 6. CLAUDE.md (user-owned — never overwrite)
|
|
410
|
+
const claudeResult = scaffoldClaudeMd(config.projectPath, config);
|
|
411
|
+
if (claudeResult === 'created') {
|
|
412
|
+
console.log(c.green(` [+] CLAUDE.md: project rules with agent roster`));
|
|
413
|
+
}
|
|
414
|
+
else {
|
|
415
|
+
console.log(c.dim(` [=] CLAUDE.md: preserved (existing file kept)`));
|
|
416
|
+
}
|
|
417
|
+
// 7. gruai.config.json (gruai-owned — always update)
|
|
418
|
+
const configResult = scaffoldGruaiConfig(config.projectPath, config);
|
|
419
|
+
console.log(c.green(` [+] Config: gruai.config.json (${configResult})`));
|
|
420
|
+
// 8. Welcome directive (user-owned — skip if exists)
|
|
421
|
+
const welcomeCreated = scaffoldWelcomeDirective(config.projectPath);
|
|
422
|
+
if (welcomeCreated) {
|
|
423
|
+
console.log(c.green(` [+] Directive: welcome directive scaffolded`));
|
|
424
|
+
}
|
|
319
425
|
// Output team summary
|
|
320
426
|
console.log(`\n ${c.bold('Done!')} gruai scaffolded into: ${c.cyan(config.projectPath)}\n`);
|
|
321
427
|
console.log(` ${c.bold('Your team:')}\n`);
|
|
@@ -333,10 +439,10 @@ export async function runScaffold(config) {
|
|
|
333
439
|
${c.dim(path.join(config.projectPath, '.context', 'preferences.md'))}
|
|
334
440
|
|
|
335
441
|
3. Start the dashboard:
|
|
336
|
-
${c.cyan('gru-ai start')}
|
|
442
|
+
${c.cyan('npx gru-ai start')}
|
|
337
443
|
|
|
338
|
-
4.
|
|
339
|
-
${c.cyan('claude -p "/directive
|
|
444
|
+
4. Give your first directive:
|
|
445
|
+
${c.cyan('claude -p "/directive add dark mode to the dashboard"')}
|
|
340
446
|
|
|
341
447
|
${c.dim('Happy orchestrating!')}
|
|
342
448
|
`);
|
|
@@ -14,15 +14,15 @@ function printStartHelp() {
|
|
|
14
14
|
${c.bold('gruai start')} — Launch the dashboard server
|
|
15
15
|
|
|
16
16
|
${c.bold('Usage:')}
|
|
17
|
-
gru-ai start [options]
|
|
17
|
+
npx gru-ai start [options]
|
|
18
18
|
|
|
19
19
|
${c.bold('Options:')}
|
|
20
20
|
--port <port> Server port (default: 4444)
|
|
21
21
|
--help Show this help message
|
|
22
22
|
|
|
23
23
|
${c.bold('Examples:')}
|
|
24
|
-
gru-ai start
|
|
25
|
-
gru-ai start --port 8080
|
|
24
|
+
npx gru-ai start
|
|
25
|
+
npx gru-ai start --port 8080
|
|
26
26
|
`);
|
|
27
27
|
}
|
|
28
28
|
function isInitialized(projectPath) {
|