juno-code 1.0.36 → 1.0.37

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 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 installed = await ScriptInstaller2.autoInstallMissing(process.cwd(), true);
24384
- if (installed && (process.argv.includes("--verbose") || process.argv.includes("-v") || process.env.JUNO_CODE_DEBUG === "1")) {
24385
- console.error("[DEBUG] Project scripts auto-installed to .juno_task/scripts/");
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-install failed:", error instanceof Error ? error.message : String(error));
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");