@elu-labs/elu 0.1.0 → 0.1.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 +52 -39
- package/lib/default-bots.mjs +1 -1
- package/lib/terminal-bots.mjs +1 -1
- package/lib/terminal-visuals.mjs +4 -2
- package/lib/terminal.mjs +33 -11
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,50 +5,20 @@ use, with bots that review and do work, and teammates you can ask.
|
|
|
5
5
|
|
|
6
6
|

|
|
7
7
|
|
|
8
|
-
Sessions run in the vendors' own unmodified command lines (Claude Code, Codex, Antigravity
|
|
9
|
-
and Grok Build), with your existing sign-in. ELU Code has no account and no telemetry, and
|
|
10
|
-
it never reads your Claude Code sign-in. The Usage tab shows Claude plan usage from what
|
|
11
|
-
Claude Code reports during your sessions. For Codex plan usage, ELU Code starts Codex's own
|
|
12
|
-
`app-server`, which asks with your Codex sign-in, whenever `codex` is installed: when you
|
|
13
|
-
open a project that is set up or send a message, at most once a minute while a session is
|
|
14
|
-
on screen, and from the Usage tab ([what is read and when](SECURITY.md#credentials)).
|
|
15
|
-
|
|
16
8
|
## Install
|
|
17
9
|
|
|
18
|
-
|
|
19
|
-
installed and signed in: Claude Code (`claude`) or Codex (`codex`).
|
|
20
|
-
|
|
21
|
-
Codex must be installed even if your sessions run on Claude Code, but it does not need to
|
|
22
|
-
be signed in. ELU Code runs your project's checks and its pre-push hooks inside Codex's
|
|
23
|
-
sandbox (`codex sandbox`); without it the checks fail and `/publish` refuses. Install it
|
|
24
|
-
with `npm install --global @openai/codex`. To use a copy that is not on your PATH, set
|
|
25
|
-
`ELU_CODE_SANDBOX_HELPER` to its absolute path. On Linux that sandbox is bubblewrap:
|
|
26
|
-
install `bubblewrap` and, on Ubuntu 24.04, load the AppArmor profile in
|
|
27
|
-
[docs/permissions.md](docs/permissions.md#linux). The Linux tests run with both.
|
|
28
|
-
|
|
29
|
-
GitHub CLI (`gh`) is needed for draft pull requests, and for `@elu` to review and merge a
|
|
30
|
-
fix pull request.
|
|
10
|
+
From your home folder, not inside a project you do not trust:
|
|
31
11
|
|
|
32
12
|
```sh
|
|
33
13
|
npx -y @elu-labs/elu
|
|
34
14
|
```
|
|
35
15
|
|
|
36
|
-
|
|
37
|
-
the `npm install --global` line it runs, and uses `~/.local` when npm's global folder
|
|
38
|
-
cannot be written. From then on you type `elu`. To open ELU Code without installing
|
|
39
|
-
anything, put `ELU_NO_INSTALL=1` in front of that line.
|
|
16
|
+
This opens ELU Code and installs the `elu` command; next time you type `elu`.
|
|
40
17
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
Antigravity (`agy`) and Grok Build (`grok`) are checked once on each machine: your first
|
|
46
|
-
message to one starts the check by itself. It takes about seven short turns on that vendor
|
|
47
|
-
and a few minutes. Vendor notes: [docs/vendors](docs/vendors).
|
|
48
|
-
|
|
49
|
-
Google's FAQ says that using another app to reach Antigravity breaks its terms and can get
|
|
50
|
-
your account suspended or terminated. ELU Code wraps `agy`, so read
|
|
51
|
-
[docs/vendors/antigravity.md](docs/vendors/antigravity.md) before you send it a message.
|
|
18
|
+
You need macOS or Linux, Node 22 or newer, Git, Claude Code signed in, and Codex installed
|
|
19
|
+
(no sign-in needed; its sandbox runs your project's checks):
|
|
20
|
+
`npm install --global @openai/codex`. Using Antigravity? Read
|
|
21
|
+
[its page](docs/vendors/antigravity.md) first. More in [Install details](#install-details).
|
|
52
22
|
|
|
53
23
|
## Start
|
|
54
24
|
|
|
@@ -206,8 +176,9 @@ Five come with the app. You can edit or remove any of them in the Bots tab.
|
|
|
206
176
|
it, and so does `/bots color qa gold`. `/bots color qa random` picks another.
|
|
207
177
|
- `@elu` starts when it joins a session and then runs there every hour. Once ELU Code is
|
|
208
178
|
installed, it runs while ELU Code is closed too, through the login item that runs
|
|
209
|
-
schedules. `/bots schedule off elu` stops that in one session. It needs
|
|
210
|
-
|
|
179
|
+
schedules. `/bots schedule off elu` stops that in one session. It needs ELU connected:
|
|
180
|
+
`npx -y @elu-labs/cli --offer=promo` signs you up, or in, adds ELU to your web app and
|
|
181
|
+
connects your AI tools.
|
|
211
182
|
|
|
212
183
|
## Teammates
|
|
213
184
|
|
|
@@ -245,7 +216,49 @@ its path, and it goes with the message: up to 4 pictures, 8 MB each. Your teamma
|
|
|
245
216
|
sees them when it answers.
|
|
246
217
|
|
|
247
218
|
In a channel, `/invite @kim` adds a teammate you already have. `/invite` alone makes a
|
|
248
|
-
code for someone new.
|
|
219
|
+
code for someone new; the first time, it also makes your team, named after the open project.
|
|
220
|
+
|
|
221
|
+
## Install details
|
|
222
|
+
|
|
223
|
+
Sessions run in the vendors' own unmodified command lines (Claude Code, Codex, Antigravity
|
|
224
|
+
and Grok Build), with your existing sign-in. ELU Code has no account and no telemetry, and
|
|
225
|
+
it never reads your Claude Code sign-in. The Usage tab shows Claude plan usage from what
|
|
226
|
+
Claude Code reports during your sessions. For Codex plan usage, ELU Code starts Codex's own
|
|
227
|
+
`app-server`, which asks with your Codex sign-in, whenever `codex` is installed: when you
|
|
228
|
+
open a project that is set up or send a message, at most once a minute while a session is
|
|
229
|
+
on screen, and from the Usage tab ([what is read and when](SECURITY.md#credentials)).
|
|
230
|
+
|
|
231
|
+
You need macOS or Linux, Node 22 or newer, Git, and Claude Code (`claude`) installed and
|
|
232
|
+
signed in: new projects and the built-in bots run on it. To run sessions on Codex
|
|
233
|
+
(`codex`), sign in to it and choose it with `/model`.
|
|
234
|
+
|
|
235
|
+
Codex must be installed even if your sessions run on Claude Code, but it does not need to
|
|
236
|
+
be signed in. ELU Code runs your project's checks and its pre-push hooks inside Codex's
|
|
237
|
+
sandbox (`codex sandbox`); without it the checks fail and `/publish` refuses. Install it
|
|
238
|
+
with `npm install --global @openai/codex`. To use a copy that is not on your PATH, set
|
|
239
|
+
`ELU_CODE_SANDBOX_HELPER` to its absolute path. On Linux that sandbox is bubblewrap:
|
|
240
|
+
install `bubblewrap` and, on Ubuntu 24.04, load the AppArmor profile in
|
|
241
|
+
[docs/permissions.md](docs/permissions.md#linux). The Linux tests run with both.
|
|
242
|
+
|
|
243
|
+
GitHub CLI (`gh`) is needed for draft pull requests, and for `@elu` to review and merge a
|
|
244
|
+
fix pull request.
|
|
245
|
+
|
|
246
|
+
The first run of `npx -y @elu-labs/elu` opens ELU Code and installs `elu` as a global
|
|
247
|
+
command: it prints the `npm install --global` line it runs, and uses `~/.local` when npm's
|
|
248
|
+
global folder cannot be written. To open ELU Code without installing anything, run
|
|
249
|
+
`ELU_NO_INSTALL=1 npx -y @elu-labs/elu`.
|
|
250
|
+
|
|
251
|
+
Run `npx -y @elu-labs/elu` from your home folder, not from inside a project you do not
|
|
252
|
+
trust: `npx` lets the folder it starts in decide what runs.
|
|
253
|
+
|
|
254
|
+
The Usage tab lists the vendor command lines ELU Code found and whether each is signed in.
|
|
255
|
+
Antigravity (`agy`) and Grok Build (`grok`) are checked once on each machine: your first
|
|
256
|
+
message to one starts the check by itself. It takes about seven short turns on that vendor
|
|
257
|
+
and a few minutes. Vendor notes: [docs/vendors](docs/vendors).
|
|
258
|
+
|
|
259
|
+
Google's FAQ says that using another app to reach Antigravity breaks its terms and can get
|
|
260
|
+
your account suspended or terminated. ELU Code wraps `agy`, so read
|
|
261
|
+
[docs/vendors/antigravity.md](docs/vendors/antigravity.md) before you send it a message.
|
|
249
262
|
|
|
250
263
|
## Update and remove
|
|
251
264
|
|
package/lib/default-bots.mjs
CHANGED
|
@@ -125,7 +125,7 @@ export const DEFAULT_BOTS = Object.freeze([
|
|
|
125
125
|
{ name: 'elu', color: 'pink', about: 'Finds issues in your live product with ELU, then merges or writes the fix', schedule: 'every 1h', mcp: ['elu'], merge: true, instructions: lines(
|
|
126
126
|
'You keep this project free of the issues ELU finds in the live product. ELU (elu.dev) watches real user sessions and reports the problems people run into. You reach it through the MCP server named elu.',
|
|
127
127
|
'On every run:',
|
|
128
|
-
'1. If you have no tools named mcp__elu__*, say that ELU is not connected, that `npx @elu-labs/
|
|
128
|
+
'1. If you have no tools named mcp__elu__*, say that ELU is not connected, that `npx -y @elu-labs/cli --offer=promo` connects it (it signs you up, or in if you have an account, adds ELU to the web app and connects your AI tools), and stop. Do not run it yourself.',
|
|
129
129
|
'2. List the open issues with list_issues, following nextOffset until it is null. Keep the ones that belong to this repository. If there are none, say so in one line and stop.',
|
|
130
130
|
'3. Take the most severe issue you have not handled in this session. Handle one issue per run.',
|
|
131
131
|
'4. If the issue already has a fix pull request (ELU opens one when self-healing is on): read the evidence for the issue, then read the pull request, its diff and its checks with gh. Review it as a careful colleague would: does it fix the reported problem, in the right place, completely, and without touching anything else?',
|
package/lib/terminal-bots.mjs
CHANGED
|
@@ -76,7 +76,7 @@ export function botNotes(bot, servers) {
|
|
|
76
76
|
bot.subagents ? botWorks(bot) ? "Its helpers work under the session's permissions, as the bot does." : 'Its helpers start only when the bot works. Give it a schedule to make it work.' : null,
|
|
77
77
|
botScheduleSpec(bot) ? `Runs by itself in each session it is in. /bots schedule off ${bot.name} stops it in one session.` : botScheduleProblem(bot.schedule),
|
|
78
78
|
bot.mcp.length && bot.vendor !== 'claude' ? onlyClaude(bot.vendor) : null,
|
|
79
|
-
missing.length ? `${notSetUp(missing)}${missing.includes('elu') ? ` npx @elu-labs/
|
|
79
|
+
missing.length ? `${notSetUp(missing)}${missing.includes('elu') ? ` npx -y @elu-labs/cli --offer=promo connects ${missing.length > 1 ? 'elu' : 'it'}: it signs you up, or in, adds ELU to your web app and connects your AI tools.` : ''}` : null,
|
|
80
80
|
bot.merge ? 'It may merge pull requests. Ask for approval mode still asks you before each merge.' : null,
|
|
81
81
|
bot.shadow ? botShadows(bot) ? SHADOW_NOTE : 'This bot works in its sessions, so it does not speak after each turn.' : null,
|
|
82
82
|
].filter(Boolean);
|
package/lib/terminal-visuals.mjs
CHANGED
|
@@ -112,10 +112,12 @@ export function threadTarget(item, status) {
|
|
|
112
112
|
const named = teams.flatMap(team => rows(team.members)).find(m => m.key !== status?.key && peerName(m.name, m.key) === peerName(item?.name));
|
|
113
113
|
return named ? dmTarget(named.key) : teams[0] ? roomTarget(teams[0].id, null) : null;
|
|
114
114
|
}
|
|
115
|
+
// What the Team list says before there is a team: how to invite, and how to join.
|
|
116
|
+
export const NO_TEAM = 'No team yet. Invite someone: /invite\nGot a code? /join CODE';
|
|
115
117
|
export function chatBar({ status, items, trusted = [], bots = [] }, width, colour) {
|
|
116
118
|
const line = (text, tone, bold) => ink(fitWords(text, width), tone, colour, bold);
|
|
117
119
|
const teams = rows(status?.teams), trust = new Set(Array.isArray(trusted) ? trusted : []), bar = [], people = new Map(), waiting = new Map(), toneOf = peopleTones(status);
|
|
118
|
-
if (!teams.length) return
|
|
120
|
+
if (!teams.length) return wrapText(NO_TEAM, width).map(text => ({ line: line(text, 'muted'), value: null }));
|
|
119
121
|
// Threads that wait for the owner are counted on their row.
|
|
120
122
|
for (const item of rows(items)) if (NEEDS_OWNER.includes(item.kind)) { const target = threadTarget(item, status); waiting.set(target, (waiting.get(target) ?? 0) + 1); }
|
|
121
123
|
// Who is in a channel, by name: me first, then whoever is online, then the rest. Only people still on the team count.
|
|
@@ -204,7 +206,7 @@ export function teamPanes({ status, agents, items, trusted = [], bots = [] }, wi
|
|
|
204
206
|
if (!threads.length) {
|
|
205
207
|
// An empty channel also says who is in it and how that changes. Sentences share a row while they fit.
|
|
206
208
|
const open = team.find(row => row.value?.target === target)?.value, room = open?.type === 'room' ? open : null;
|
|
207
|
-
const says = !where ? ['No
|
|
209
|
+
const says = !where ? ['No team yet. Invite someone with /invite, or join with /join CODE.'] : [`Nothing in ${where} yet.`, ...(
|
|
208
210
|
!room ? ['Type below to write to it.']
|
|
209
211
|
: room.channel ? ['Type below to write to it.', 'm changes who is in it.']
|
|
210
212
|
: room.people.length < 2 ? ['Nobody else is on the team yet.', '+ Add teammate invites someone.']
|
package/lib/terminal.mjs
CHANGED
|
@@ -111,6 +111,8 @@ export function usageDetailLines(row, now = new Date()) {
|
|
|
111
111
|
}
|
|
112
112
|
export const geometry = terminalLayout;
|
|
113
113
|
const PEER_OFF = 'Peer mode is off. Type /peer on to start it.';
|
|
114
|
+
// Chats with peer mode off: /invite and /join turn it on themselves.
|
|
115
|
+
const CHATS_OFF = `${PEER_OFF}\nInvite someone: /invite\nGot a code? /join CODE`;
|
|
114
116
|
const NO_PROJECT = 'Create a project first: press n in the Projects tab.';
|
|
115
117
|
const PEER_STOPPED = 'The peer daemon is not running. Run elu peer on.';
|
|
116
118
|
const PEER_MISSING = 'Peer mode needs the optional networking packages. Reinstall with optional dependencies.';
|
|
@@ -931,7 +933,7 @@ export async function launchTerminal({ repo, startDirectory = process.cwd(), inp
|
|
|
931
933
|
// Chats
|
|
932
934
|
teamOn = view === 'chats' && peerData.on;
|
|
933
935
|
teamOff.hidden = view !== 'chats' || teamOn;
|
|
934
|
-
teamOff.setContent(facade ? stopped ? PEER_STOPPED :
|
|
936
|
+
teamOff.setContent(facade ? stopped ? PEER_STOPPED : CHATS_OFF : PEER_MISSING);
|
|
935
937
|
teamBoxes.forEach((frame, index) => { frame.hidden = !teamOn; frame.style.border.fg = screen.focused === teamLists[index] ? accent : shade('border'); });
|
|
936
938
|
if (teamOn) {
|
|
937
939
|
// The bar lists channels, then each teammate with their live agents. Each pane keeps its selected row while content moves.
|
|
@@ -1597,17 +1599,29 @@ export async function launchTerminal({ repo, startDirectory = process.cwd(), inp
|
|
|
1597
1599
|
}
|
|
1598
1600
|
const usageOf = name => COMMANDS.find(row => row.name === name).usage;
|
|
1599
1601
|
const hasIdentity = () => { try { facade.identity(); return true; } catch (error) { if (/identity first/.test(error.message)) return false; throw error; } };
|
|
1602
|
+
// The name teammates see, asked for the first time peer mode is needed. False when none was given.
|
|
1603
|
+
const askName = async () => {
|
|
1604
|
+
if (typeof facade.identity !== 'function' || hasIdentity()) return true;
|
|
1605
|
+
const name = (await prompt('Your name', 'Teammates see this name on your asks and answers. 1 to 40 characters.', os.userInfo().username))?.trim();
|
|
1606
|
+
if (!name) return false;
|
|
1607
|
+
facade.identity({ create: true, name }); return true;
|
|
1608
|
+
};
|
|
1609
|
+
// Making or using a team code needs peer mode on: a new person gets the name prompt and the daemon started here.
|
|
1610
|
+
const peerReady = async () => {
|
|
1611
|
+
if (!(await askName())) { setMessage('Nothing was done. Your teammates need a name for you.'); return false; }
|
|
1612
|
+
if (peerRead('peerStatus')?.running === true) return true;
|
|
1613
|
+
setMessage('Starting peer mode…'); draw(); await facade.startPeer();
|
|
1614
|
+
for (let i = 0; i < 40 && peerRead('peerStatus')?.running !== true; i++) await new Promise(done => setTimeout(done, 250));
|
|
1615
|
+
if (peerRead('peerStatus')?.running !== true) throw new Error(PEER_STOPPED);
|
|
1616
|
+
return true;
|
|
1617
|
+
};
|
|
1600
1618
|
// Peer slash commands. Each makes one facade call; the result shows in the status line.
|
|
1601
1619
|
async function peerSlash({ name, args, text = '' }) {
|
|
1602
1620
|
if (!facade) throw new Error(PEER_MISSING);
|
|
1603
1621
|
if (name === 'peer') {
|
|
1604
1622
|
if (args.length > 1 || args[0] && !['on', 'off'].includes(args[0])) throw new Error('Use /peer, /peer on or /peer off.');
|
|
1605
1623
|
const turn = args[0] ?? (peerOn() ? 'off' : 'on');
|
|
1606
|
-
if (turn === 'on' &&
|
|
1607
|
-
const name = (await prompt('Your name', 'Teammates see this name on your asks and answers. 1 to 40 characters.', os.userInfo().username))?.trim();
|
|
1608
|
-
if (!name) { setMessage('Peer mode stays off. /peer on asks for your name again.'); return; }
|
|
1609
|
-
facade.identity({ create: true, name });
|
|
1610
|
-
}
|
|
1624
|
+
if (turn === 'on' && !(await askName())) { setMessage('Peer mode stays off. /peer on asks for your name again.'); return; }
|
|
1611
1625
|
await (turn === 'on' ? facade.startPeer() : facade.stopPeer()); setMessage(`Peer mode is ${turn}.`); return;
|
|
1612
1626
|
}
|
|
1613
1627
|
if (name === 'share') {
|
|
@@ -1616,7 +1630,7 @@ export async function launchTerminal({ repo, startDirectory = process.cwd(), inp
|
|
|
1616
1630
|
controller.writable(); const result = await facade.share(controller.repo.root, { handle: args[0], status: true });
|
|
1617
1631
|
setMessage(`Shared ${shortHome(result?.root ?? controller.repo.root)} with your team as ${remote(result?.handle, 40) || args[0] || path.basename(controller.repo.root)}.`); return;
|
|
1618
1632
|
}
|
|
1619
|
-
if (!peerOn()) throw new Error(PEER_OFF);
|
|
1633
|
+
if (!peerOn() && !['invite', 'join'].includes(name)) throw new Error(PEER_OFF);
|
|
1620
1634
|
if (name === 'peers') {
|
|
1621
1635
|
const status = peerRead('peerStatus'), members = new Map();
|
|
1622
1636
|
for (const member of (status?.teams ?? []).flatMap(row => row?.members ?? [])) if (member?.key && member.key !== status.key) members.set(member.key, member);
|
|
@@ -1629,21 +1643,29 @@ export async function launchTerminal({ repo, startDirectory = process.cwd(), inp
|
|
|
1629
1643
|
try { controller.writable(); const row = await facade.share(controller.repo.root, { teams: [teamId] }); return `Teammates can ask about ${remote(row?.handle, 40) || path.basename(controller.repo.root)}, the folder ${shortHome(row?.root ?? controller.repo.root)}.`; }
|
|
1630
1644
|
catch (error) { return `${path.basename(controller.repo.root)} is not shared: ${error.message}`; }
|
|
1631
1645
|
};
|
|
1646
|
+
const firstTeam = async () => {
|
|
1647
|
+
const named = controller ? path.basename(controller.repo.root).slice(0, 60) : '';
|
|
1648
|
+
try { if (named) return await facade.createTeam(named); } catch {}
|
|
1649
|
+
return facade.createTeam('My team');
|
|
1650
|
+
};
|
|
1632
1651
|
if (name === 'invite') {
|
|
1633
1652
|
const teams = peerRead('peerStatus')?.teams ?? [];
|
|
1653
|
+
if (!teams.length && args.length) throw new Error('No team yet. /invite alone starts one and makes a code to send.');
|
|
1654
|
+
if (!(await peerReady())) return;
|
|
1634
1655
|
// A name adds a teammate to the open channel: @name always, a bare word unless a team is called that.
|
|
1635
1656
|
if (teams.length && args.length === 1 && (args[0].startsWith('@') || !teams.some(row => row.id === args[0] || row.name === args[0]))) return inviteToChannel(args[0]);
|
|
1636
|
-
|
|
1637
|
-
|
|
1657
|
+
// The first /invite makes the team, so nobody has to name one: it is named after the open project.
|
|
1658
|
+
const chosen = args.length ? teams.find(row => row.id === args[0] || row.name === args.join(' ')) : teams[0] ?? await firstTeam();
|
|
1659
|
+
if (!chosen) throw new Error('No team by that name.');
|
|
1638
1660
|
const result = await facade.createInvite(chosen.id, { hours: 24 }), code = typeof result === 'string' ? result : result?.code;
|
|
1639
1661
|
if (typeof code !== 'string' || !code.startsWith('elu-team:')) throw new Error('The invite could not be made.');
|
|
1640
1662
|
const shared = await teamShare(chosen.id), bots = await shareBots();
|
|
1641
1663
|
setMessage(`Invite for ${remote(chosen.name, 40)} ready. It works once, within 24 h.`);
|
|
1642
|
-
await prompt('Team invite', `${code}\n\nOne use, expires in 24 h.\n${shared}\n${bots ? `${bots}\n` : ''}
|
|
1664
|
+
await prompt('Team invite', `${code}\n\nOne use, expires in 24 h.\n${shared}\n${bots ? `${bots}\n` : ''}Send this code to your teammate. On their computer:\n1. Open a terminal in the project folder they will share with you.\n2. Run npx -y @elu-labs/elu\n3. In ELU Code, type /join and this code.\nStay online until they join.`); return;
|
|
1643
1665
|
}
|
|
1644
1666
|
if (name === 'join') {
|
|
1645
1667
|
if (args.length !== 1 || !args[0].startsWith('elu-team:')) throw new Error(`Use ${usageOf('join')}.`);
|
|
1646
|
-
if (
|
|
1668
|
+
if (!(await peerReady())) return;
|
|
1647
1669
|
setMessage('Joining the team…'); draw();
|
|
1648
1670
|
const joined = await facade.joinTeam(args[0]);
|
|
1649
1671
|
const shared = await teamShare(joined?.id), bots = await shareBots();
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elu-labs/elu",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@elu-labs/elu",
|
|
9
|
-
"version": "0.1.
|
|
9
|
+
"version": "0.1.1",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"os": [
|
|
12
12
|
"darwin",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elu-labs/elu",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "A full-screen terminal app that runs coding sessions on the AI command lines you already use, with bots that review and do work, and teammates you can ask.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|