juno-code 1.0.36 → 1.0.38
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 +170 -0
- package/dist/bin/cli.js +143 -5
- package/dist/bin/cli.js.map +1 -1
- package/dist/bin/cli.mjs +143 -5
- package/dist/bin/cli.mjs.map +1 -1
- package/dist/index.js +18 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +18 -1
- package/dist/index.mjs.map +1 -1
- package/dist/templates/scripts/install_requirements.sh +3 -0
- package/dist/templates/scripts/run_until_completion.sh +227 -11
- package/dist/templates/scripts/slack_fetch.py +717 -0
- package/dist/templates/scripts/slack_fetch.sh +269 -0
- package/dist/templates/scripts/slack_respond.py +693 -0
- package/dist/templates/scripts/slack_respond.sh +263 -0
- package/dist/templates/scripts/slack_state.py +383 -0
- package/package.json +8 -2
package/README.md
CHANGED
|
@@ -192,6 +192,176 @@ juno-code feedback --interactive
|
|
|
192
192
|
juno-code start -b shell -s claude --enable-feedback -i 10
|
|
193
193
|
```
|
|
194
194
|
|
|
195
|
+
## Slack Integration
|
|
196
|
+
|
|
197
|
+
juno-code includes built-in Slack integration for team collaboration. The system monitors Slack channels and creates kanban tasks from messages, then posts agent responses as threaded replies.
|
|
198
|
+
|
|
199
|
+
### How It Works
|
|
200
|
+
|
|
201
|
+
1. **Fetch**: `slack_fetch.sh` monitors a Slack channel and creates kanban tasks from new messages
|
|
202
|
+
2. **Process**: The AI agent processes tasks and records responses in the kanban
|
|
203
|
+
3. **Respond**: `slack_respond.sh` sends agent responses back to Slack as threaded replies
|
|
204
|
+
|
|
205
|
+
This enables a workflow where team members can submit tasks via Slack and receive AI-generated responses without leaving their chat interface.
|
|
206
|
+
|
|
207
|
+
### Setup
|
|
208
|
+
|
|
209
|
+
1. **Create a Slack App**:
|
|
210
|
+
- Go to https://api.slack.com/apps and create a new app
|
|
211
|
+
- Under "OAuth & Permissions", add these scopes:
|
|
212
|
+
- `channels:history`, `channels:read` (public channels)
|
|
213
|
+
- `groups:history`, `groups:read` (private channels)
|
|
214
|
+
- `users:read` (user info)
|
|
215
|
+
- `chat:write` (send messages)
|
|
216
|
+
- Install the app to your workspace
|
|
217
|
+
- Copy the "Bot User OAuth Token" (starts with `xoxb-`)
|
|
218
|
+
|
|
219
|
+
2. **Configure Environment**:
|
|
220
|
+
```bash
|
|
221
|
+
# In project root .env file
|
|
222
|
+
SLACK_BOT_TOKEN=xoxb-your-token-here
|
|
223
|
+
SLACK_CHANNEL=bug-reports
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
3. **Usage**:
|
|
227
|
+
```bash
|
|
228
|
+
# Fetch messages from Slack and create tasks
|
|
229
|
+
./.juno_task/scripts/slack_fetch.sh --channel bug-reports
|
|
230
|
+
|
|
231
|
+
# Continuous monitoring mode
|
|
232
|
+
./.juno_task/scripts/slack_fetch.sh --channel feature-requests --continuous
|
|
233
|
+
|
|
234
|
+
# Send completed task responses back to Slack
|
|
235
|
+
./.juno_task/scripts/slack_respond.sh --tag slack-input
|
|
236
|
+
|
|
237
|
+
# Dry run to preview what would be sent
|
|
238
|
+
./.juno_task/scripts/slack_respond.sh --dry-run --verbose
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Automated Slack Workflow with Hooks
|
|
242
|
+
|
|
243
|
+
Use the `--pre-run` flag to sync with Slack before each juno-code run:
|
|
244
|
+
|
|
245
|
+
```bash
|
|
246
|
+
# Fetch Slack messages before starting work
|
|
247
|
+
./.juno_task/scripts/run_until_completion.sh \
|
|
248
|
+
--pre-run "./.juno_task/scripts/slack_fetch.sh --channel bug-reports" \
|
|
249
|
+
-s claude -i 5 -v
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
Or configure hooks in `.juno_task/config.json`:
|
|
253
|
+
|
|
254
|
+
```json
|
|
255
|
+
{
|
|
256
|
+
"hooks": {
|
|
257
|
+
"SLACK_SYNC": {
|
|
258
|
+
"commands": [
|
|
259
|
+
"./.juno_task/scripts/slack_fetch.sh --channel bug-reports",
|
|
260
|
+
"./.juno_task/scripts/slack_respond.sh --tag slack-input"
|
|
261
|
+
]
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Then run with the hook:
|
|
268
|
+
|
|
269
|
+
```bash
|
|
270
|
+
./.juno_task/scripts/run_until_completion.sh --pre-run-hook SLACK_SYNC -s claude -i 5 -v
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## run_until_completion.sh
|
|
274
|
+
|
|
275
|
+
The `run_until_completion.sh` script continuously runs juno-code until all kanban tasks are completed. It uses a do-while loop pattern: juno-code runs at least once, then continues while tasks remain in backlog, todo, or in_progress status.
|
|
276
|
+
|
|
277
|
+
### Basic Usage
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
# Run until all tasks complete
|
|
281
|
+
./.juno_task/scripts/run_until_completion.sh -s claude -i 5 -v
|
|
282
|
+
|
|
283
|
+
# With custom backend and model
|
|
284
|
+
./.juno_task/scripts/run_until_completion.sh -b shell -s codex -m :codex -i 10
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### Pre-run Commands (--pre-run)
|
|
288
|
+
|
|
289
|
+
Execute commands before the main loop starts. Useful for syncing with external services, running linters, or preparing the environment.
|
|
290
|
+
|
|
291
|
+
```bash
|
|
292
|
+
# Single pre-run command
|
|
293
|
+
./.juno_task/scripts/run_until_completion.sh --pre-run "./scripts/lint.sh" -s claude -i 5
|
|
294
|
+
|
|
295
|
+
# Multiple pre-run commands (executed in order)
|
|
296
|
+
./.juno_task/scripts/run_until_completion.sh \
|
|
297
|
+
--pre-run "./scripts/sync.sh" \
|
|
298
|
+
--pre-run "npm run build" \
|
|
299
|
+
-s claude -i 5 -v
|
|
300
|
+
|
|
301
|
+
# Alternative: use environment variable
|
|
302
|
+
JUNO_PRE_RUN="./scripts/prepare.sh" \
|
|
303
|
+
./.juno_task/scripts/run_until_completion.sh -s claude -i 5
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### Pre-run Hooks (--pre-run-hook)
|
|
307
|
+
|
|
308
|
+
Execute named hooks defined in `.juno_task/config.json`. Hooks group multiple commands that run together.
|
|
309
|
+
|
|
310
|
+
**Define hooks in config.json:**
|
|
311
|
+
```json
|
|
312
|
+
{
|
|
313
|
+
"hooks": {
|
|
314
|
+
"START_ITERATION": {
|
|
315
|
+
"commands": [
|
|
316
|
+
"./scripts/lint.sh",
|
|
317
|
+
"npm run typecheck"
|
|
318
|
+
]
|
|
319
|
+
},
|
|
320
|
+
"SLACK_SYNC": {
|
|
321
|
+
"commands": [
|
|
322
|
+
"./.juno_task/scripts/slack_fetch.sh --channel bugs",
|
|
323
|
+
"./.juno_task/scripts/slack_respond.sh"
|
|
324
|
+
]
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
**Use hooks:**
|
|
331
|
+
```bash
|
|
332
|
+
# Single hook
|
|
333
|
+
./.juno_task/scripts/run_until_completion.sh --pre-run-hook START_ITERATION -s claude -i 5
|
|
334
|
+
|
|
335
|
+
# Multiple hooks (executed in order)
|
|
336
|
+
./.juno_task/scripts/run_until_completion.sh \
|
|
337
|
+
--pre-run-hook SLACK_SYNC \
|
|
338
|
+
--pre-run-hook START_ITERATION \
|
|
339
|
+
-s claude -i 5
|
|
340
|
+
|
|
341
|
+
# Alternative: use environment variable
|
|
342
|
+
JUNO_PRE_RUN_HOOK="START_ITERATION" \
|
|
343
|
+
./.juno_task/scripts/run_until_completion.sh -s claude -i 5
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
### Execution Order
|
|
347
|
+
|
|
348
|
+
When both hooks and pre-run commands are specified, the execution order is:
|
|
349
|
+
1. Hooks from `JUNO_PRE_RUN_HOOK` env var
|
|
350
|
+
2. Hooks from `--pre-run-hook` flags (in order)
|
|
351
|
+
3. Commands from `JUNO_PRE_RUN` env var
|
|
352
|
+
4. Commands from `--pre-run` flags (in order)
|
|
353
|
+
5. Main juno-code loop begins
|
|
354
|
+
|
|
355
|
+
### Environment Variables
|
|
356
|
+
|
|
357
|
+
| Variable | Description |
|
|
358
|
+
|----------|-------------|
|
|
359
|
+
| `JUNO_DEBUG=true` | Show debug diagnostic messages |
|
|
360
|
+
| `JUNO_VERBOSE=true` | Show informational status messages |
|
|
361
|
+
| `JUNO_PRE_RUN` | Pre-run command (runs before --pre-run flags) |
|
|
362
|
+
| `JUNO_PRE_RUN_HOOK` | Pre-run hook name (runs before --pre-run-hook flags) |
|
|
363
|
+
| `JUNO_RUN_UNTIL_MAX_ITERATIONS` | Maximum iterations (0 = unlimited) |
|
|
364
|
+
|
|
195
365
|
## Kanban Commands
|
|
196
366
|
|
|
197
367
|
The kanban.sh script wraps juno-kanban. Here are the actual commands:
|
package/dist/bin/cli.js
CHANGED
|
@@ -318,6 +318,13 @@ var init_default_hooks = __esm({
|
|
|
318
318
|
"src/templates/default-hooks.ts"() {
|
|
319
319
|
init_version();
|
|
320
320
|
DEFAULT_HOOKS = {
|
|
321
|
+
// Executes once at the beginning of a run (before all iterations)
|
|
322
|
+
// Use for: setup, environment checks, notifications, pre-run cleanup
|
|
323
|
+
START_RUN: {
|
|
324
|
+
commands: []
|
|
325
|
+
},
|
|
326
|
+
// Executes at the start of each iteration
|
|
327
|
+
// Use for: file monitoring, state checks, per-iteration setup
|
|
321
328
|
START_ITERATION: {
|
|
322
329
|
commands: [
|
|
323
330
|
// Monitor CLAUDE.md file size
|
|
@@ -326,6 +333,16 @@ var init_default_hooks = __esm({
|
|
|
326
333
|
'file="AGENTS.md"; lines=$(wc -l < "$file" 2>/dev/null || echo 0); chars=$(wc -m < "$file" 2>/dev/null || echo 0); if [ "$lines" -gt 450 ] || [ "$chars" -gt 60000 ]; then juno-kanban "[Critical] file $file is too large, keep it lean and useful for every run of the agent."; fi',
|
|
327
334
|
"./.juno_task/scripts/cleanup_feedback.sh"
|
|
328
335
|
]
|
|
336
|
+
},
|
|
337
|
+
// Executes at the end of each iteration
|
|
338
|
+
// Use for: validation, logging, per-iteration cleanup, progress tracking
|
|
339
|
+
END_ITERATION: {
|
|
340
|
+
commands: []
|
|
341
|
+
},
|
|
342
|
+
// Executes once at the end of a run (after all iterations complete)
|
|
343
|
+
// Use for: final cleanup, notifications, reports, post-run actions
|
|
344
|
+
END_RUN: {
|
|
345
|
+
commands: []
|
|
329
346
|
}
|
|
330
347
|
};
|
|
331
348
|
}
|
|
@@ -13393,12 +13410,24 @@ var init_script_installer = __esm({
|
|
|
13393
13410
|
/**
|
|
13394
13411
|
* Required scripts include both standalone scripts and their dependencies.
|
|
13395
13412
|
* kanban.sh depends on install_requirements.sh for Python venv setup.
|
|
13413
|
+
* Slack integration scripts allow fetching tasks from Slack and responding.
|
|
13396
13414
|
*/
|
|
13397
13415
|
static REQUIRED_SCRIPTS = [
|
|
13398
13416
|
"run_until_completion.sh",
|
|
13399
13417
|
"kanban.sh",
|
|
13400
|
-
"install_requirements.sh"
|
|
13418
|
+
"install_requirements.sh",
|
|
13401
13419
|
// Required by kanban.sh for Python venv creation
|
|
13420
|
+
// Slack integration scripts
|
|
13421
|
+
"slack_state.py",
|
|
13422
|
+
// State management for Slack integration
|
|
13423
|
+
"slack_fetch.py",
|
|
13424
|
+
// Core logic for fetching Slack messages
|
|
13425
|
+
"slack_fetch.sh",
|
|
13426
|
+
// Wrapper script for Slack fetch
|
|
13427
|
+
"slack_respond.py",
|
|
13428
|
+
// Core logic for sending responses to Slack
|
|
13429
|
+
"slack_respond.sh"
|
|
13430
|
+
// Wrapper script for Slack respond
|
|
13402
13431
|
];
|
|
13403
13432
|
/**
|
|
13404
13433
|
* Get the templates scripts directory from the package
|
|
@@ -13587,6 +13616,115 @@ var init_script_installer = __esm({
|
|
|
13587
13616
|
}
|
|
13588
13617
|
return results;
|
|
13589
13618
|
}
|
|
13619
|
+
/**
|
|
13620
|
+
* Get scripts that need updates based on content comparison
|
|
13621
|
+
* @param projectDir - The project root directory
|
|
13622
|
+
* @returns Array of script names that have different content from package version
|
|
13623
|
+
*/
|
|
13624
|
+
static async getOutdatedScripts(projectDir) {
|
|
13625
|
+
const outdated = [];
|
|
13626
|
+
const packageScriptsDir = this.getPackageScriptsDir();
|
|
13627
|
+
if (!packageScriptsDir) {
|
|
13628
|
+
return outdated;
|
|
13629
|
+
}
|
|
13630
|
+
for (const script of this.REQUIRED_SCRIPTS) {
|
|
13631
|
+
const sourcePath = path3__namespace.join(packageScriptsDir, script);
|
|
13632
|
+
const destPath = path3__namespace.join(projectDir, ".juno_task", "scripts", script);
|
|
13633
|
+
if (!await fs3__default.default.pathExists(sourcePath)) {
|
|
13634
|
+
continue;
|
|
13635
|
+
}
|
|
13636
|
+
if (!await fs3__default.default.pathExists(destPath)) {
|
|
13637
|
+
continue;
|
|
13638
|
+
}
|
|
13639
|
+
try {
|
|
13640
|
+
const [sourceContent, destContent] = await Promise.all([
|
|
13641
|
+
fs3__default.default.readFile(sourcePath, "utf-8"),
|
|
13642
|
+
fs3__default.default.readFile(destPath, "utf-8")
|
|
13643
|
+
]);
|
|
13644
|
+
if (sourceContent !== destContent) {
|
|
13645
|
+
outdated.push(script);
|
|
13646
|
+
}
|
|
13647
|
+
} catch {
|
|
13648
|
+
outdated.push(script);
|
|
13649
|
+
}
|
|
13650
|
+
}
|
|
13651
|
+
return outdated;
|
|
13652
|
+
}
|
|
13653
|
+
/**
|
|
13654
|
+
* Check if any scripts need installation or update
|
|
13655
|
+
* @param projectDir - The project root directory
|
|
13656
|
+
* @returns true if any scripts need to be installed or updated
|
|
13657
|
+
*/
|
|
13658
|
+
static async needsUpdate(projectDir) {
|
|
13659
|
+
try {
|
|
13660
|
+
const junoTaskDir = path3__namespace.join(projectDir, ".juno_task");
|
|
13661
|
+
if (!await fs3__default.default.pathExists(junoTaskDir)) {
|
|
13662
|
+
return false;
|
|
13663
|
+
}
|
|
13664
|
+
const missing = await this.getMissingScripts(projectDir);
|
|
13665
|
+
if (missing.length > 0) {
|
|
13666
|
+
return true;
|
|
13667
|
+
}
|
|
13668
|
+
const outdated = await this.getOutdatedScripts(projectDir);
|
|
13669
|
+
return outdated.length > 0;
|
|
13670
|
+
} catch {
|
|
13671
|
+
return false;
|
|
13672
|
+
}
|
|
13673
|
+
}
|
|
13674
|
+
/**
|
|
13675
|
+
* Automatically update scripts - installs missing AND updates outdated scripts
|
|
13676
|
+
* Similar to ServiceInstaller.autoUpdate(), this ensures project scripts
|
|
13677
|
+
* are always in sync with the package version.
|
|
13678
|
+
*
|
|
13679
|
+
* This should be called on every CLI run to ensure scripts are up-to-date.
|
|
13680
|
+
* @param projectDir - The project root directory
|
|
13681
|
+
* @param silent - If true, suppresses console output
|
|
13682
|
+
* @returns true if any scripts were installed or updated
|
|
13683
|
+
*/
|
|
13684
|
+
static async autoUpdate(projectDir, silent = true) {
|
|
13685
|
+
try {
|
|
13686
|
+
const debug = process.env.JUNO_CODE_DEBUG === "1";
|
|
13687
|
+
const junoTaskDir = path3__namespace.join(projectDir, ".juno_task");
|
|
13688
|
+
if (!await fs3__default.default.pathExists(junoTaskDir)) {
|
|
13689
|
+
return false;
|
|
13690
|
+
}
|
|
13691
|
+
const missing = await this.getMissingScripts(projectDir);
|
|
13692
|
+
const outdated = await this.getOutdatedScripts(projectDir);
|
|
13693
|
+
if (debug) {
|
|
13694
|
+
if (missing.length > 0) {
|
|
13695
|
+
console.error(`[DEBUG] ScriptInstaller: Missing scripts: ${missing.join(", ")}`);
|
|
13696
|
+
}
|
|
13697
|
+
if (outdated.length > 0) {
|
|
13698
|
+
console.error(`[DEBUG] ScriptInstaller: Outdated scripts: ${outdated.join(", ")}`);
|
|
13699
|
+
}
|
|
13700
|
+
}
|
|
13701
|
+
if (missing.length === 0 && outdated.length === 0) {
|
|
13702
|
+
return false;
|
|
13703
|
+
}
|
|
13704
|
+
const scriptsToUpdate = [.../* @__PURE__ */ new Set([...missing, ...outdated])];
|
|
13705
|
+
let updatedAny = false;
|
|
13706
|
+
for (const script of scriptsToUpdate) {
|
|
13707
|
+
const installed = await this.installScript(projectDir, script, silent);
|
|
13708
|
+
if (installed) {
|
|
13709
|
+
updatedAny = true;
|
|
13710
|
+
}
|
|
13711
|
+
}
|
|
13712
|
+
if (updatedAny) {
|
|
13713
|
+
if (debug) {
|
|
13714
|
+
console.error(`[DEBUG] ScriptInstaller: Updated ${scriptsToUpdate.length} script(s)`);
|
|
13715
|
+
}
|
|
13716
|
+
if (!silent) {
|
|
13717
|
+
console.log(`\u2713 Updated ${scriptsToUpdate.length} script(s) in .juno_task/scripts/`);
|
|
13718
|
+
}
|
|
13719
|
+
}
|
|
13720
|
+
return updatedAny;
|
|
13721
|
+
} catch (error) {
|
|
13722
|
+
if (process.env.JUNO_CODE_DEBUG === "1") {
|
|
13723
|
+
console.error("[DEBUG] ScriptInstaller: autoUpdate error:", error);
|
|
13724
|
+
}
|
|
13725
|
+
return false;
|
|
13726
|
+
}
|
|
13727
|
+
}
|
|
13590
13728
|
};
|
|
13591
13729
|
}
|
|
13592
13730
|
});
|
|
@@ -24380,13 +24518,13 @@ async function main() {
|
|
|
24380
24518
|
}
|
|
24381
24519
|
try {
|
|
24382
24520
|
const { ScriptInstaller: ScriptInstaller2 } = await Promise.resolve().then(() => (init_script_installer(), script_installer_exports));
|
|
24383
|
-
const
|
|
24384
|
-
if (
|
|
24385
|
-
console.error("[DEBUG] Project scripts auto-
|
|
24521
|
+
const updated = await ScriptInstaller2.autoUpdate(process.cwd(), true);
|
|
24522
|
+
if (updated && (process.argv.includes("--verbose") || process.argv.includes("-v") || process.env.JUNO_CODE_DEBUG === "1")) {
|
|
24523
|
+
console.error("[DEBUG] Project scripts auto-updated in .juno_task/scripts/");
|
|
24386
24524
|
}
|
|
24387
24525
|
} catch (error) {
|
|
24388
24526
|
if (process.env.JUNO_CODE_DEBUG === "1") {
|
|
24389
|
-
console.error("[DEBUG] Script auto-
|
|
24527
|
+
console.error("[DEBUG] Script auto-update failed:", error instanceof Error ? error.message : String(error));
|
|
24390
24528
|
}
|
|
24391
24529
|
}
|
|
24392
24530
|
program.name("juno-code").description("TypeScript implementation of juno-code CLI tool for AI subagent orchestration").version(VERSION, "-V, --version", "Display version information").helpOption("-h, --help", "Display help information");
|