@polderlabs/bizar-plugin 0.5.4 → 0.6.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 +1 -1
- package/dist/index.js +29901 -0
- package/index.ts +94 -11
- package/package.json +1 -1
- package/src/background-state.ts +56 -4
- package/src/background.ts +166 -12
- package/src/commands-impl.ts +95 -0
- package/src/commands.ts +321 -91
- package/src/plan-fs.ts +2 -2
- package/src/reasoning-clean.ts +360 -0
- package/src/serve-info.ts +228 -0
- package/src/serve.ts +24 -4
- package/src/tools/bg-spawn.ts +21 -1
- package/tests/attach-handler-bug.test.ts +7 -5
- package/tests/background-state.test.ts +1 -1
- package/tests/background.test.ts +1 -1
- package/tests/block.test.ts +3 -1
- package/tests/canonical-key-order.test.ts +11 -7
- package/tests/event-stream.test.ts +2 -2
- package/tests/event.test.ts +1 -1
- package/tests/fingerprint.test.ts +22 -21
- package/tests/http-client.test.ts +11 -10
- package/tests/init-helpers.test.ts +3 -3
- package/tests/options.test.ts +10 -8
- package/tests/serve.test.ts +14 -10
- package/tests/settings.test.ts +2 -2
- package/tests/stall-think.test.ts +13 -12
- package/tests/state.test.ts +2 -1
- package/tests/tools/bg-get-comments.test.ts +2 -2
- package/tests/tools/bg-kill.test.ts +9 -5
- package/tests/tools/bg-spawn.test.ts +12 -12
- package/tests/tools/bg-status.test.ts +2 -1
- package/tests/tools/plan-action.test.ts +2 -2
- package/tests/tools/wait-for-feedback.test.ts +2 -2
- package/tests/update-deadlock.test.ts +144 -0
package/src/commands.ts
CHANGED
|
@@ -60,12 +60,37 @@ export type SideEffect =
|
|
|
60
60
|
| {
|
|
61
61
|
kind: "list_plans";
|
|
62
62
|
}
|
|
63
|
+
| {
|
|
64
|
+
kind: "launch_dashboard";
|
|
65
|
+
defaultPort: number;
|
|
66
|
+
}
|
|
63
67
|
| {
|
|
64
68
|
kind: "tool_invocation";
|
|
65
69
|
toolName: string;
|
|
66
70
|
args: unknown;
|
|
67
71
|
};
|
|
68
72
|
|
|
73
|
+
/** Dialog component types that can be rendered in the dashboard. */
|
|
74
|
+
export type DialogComponent =
|
|
75
|
+
| "visual-plan"
|
|
76
|
+
| "plan-create"
|
|
77
|
+
| "plan-list"
|
|
78
|
+
| "help"
|
|
79
|
+
| "audit"
|
|
80
|
+
| "generic";
|
|
81
|
+
|
|
82
|
+
export interface DialogDescriptor {
|
|
83
|
+
id: string;
|
|
84
|
+
title: string;
|
|
85
|
+
command: string;
|
|
86
|
+
component: DialogComponent;
|
|
87
|
+
data?: Record<string, unknown>;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function generateId(): string {
|
|
91
|
+
return `dlg_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
92
|
+
}
|
|
93
|
+
|
|
69
94
|
export interface SlashCommandResult {
|
|
70
95
|
handled: true;
|
|
71
96
|
/** Text shown to the user / LLM. */
|
|
@@ -74,6 +99,8 @@ export interface SlashCommandResult {
|
|
|
74
99
|
settingsPatch?: Partial<PlanSettings>;
|
|
75
100
|
/** Optional side-effect to perform (file I/O lives in the hook, not here). */
|
|
76
101
|
sideEffect?: SideEffect;
|
|
102
|
+
/** Optional dialog to open in the dashboard instead of showing text. */
|
|
103
|
+
dialog?: DialogDescriptor;
|
|
77
104
|
}
|
|
78
105
|
|
|
79
106
|
/**
|
|
@@ -264,6 +291,8 @@ export function parseSlashCommand(
|
|
|
264
291
|
return handleVisualPlan(rest, ctx);
|
|
265
292
|
case "plan":
|
|
266
293
|
return handlePlan(rest, ctx);
|
|
294
|
+
case "bizar":
|
|
295
|
+
return handleBizar(rest, ctx);
|
|
267
296
|
case "help":
|
|
268
297
|
case "commands":
|
|
269
298
|
return helpResult();
|
|
@@ -279,43 +308,82 @@ export function parseSlashCommand(
|
|
|
279
308
|
|
|
280
309
|
function handleVisualPlan(arg: string, ctx: ParseContext): SlashCommandResult {
|
|
281
310
|
const lc = arg.toLowerCase();
|
|
311
|
+
const currentEnabled = ctx.currentSettings.visualPlanEnabled;
|
|
282
312
|
|
|
283
313
|
if (lc === "") {
|
|
284
|
-
// No argument — return current state
|
|
285
|
-
const on = ctx.currentSettings.visualPlanEnabled ? "on" : "off";
|
|
314
|
+
// No argument — return current state as a dialog
|
|
286
315
|
return {
|
|
287
316
|
handled: true,
|
|
288
|
-
response:
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
317
|
+
response: "",
|
|
318
|
+
dialog: {
|
|
319
|
+
id: generateId(),
|
|
320
|
+
title: "Visual Plan",
|
|
321
|
+
command: "/visual-plan",
|
|
322
|
+
component: "visual-plan",
|
|
323
|
+
data: {
|
|
324
|
+
enabled: currentEnabled,
|
|
325
|
+
previousEnabled: currentEnabled,
|
|
326
|
+
defaultTemplate: ctx.currentSettings.defaultTemplate,
|
|
327
|
+
lastUsedSlug: ctx.currentSettings.lastUsedSlug ?? null,
|
|
328
|
+
mode: "status",
|
|
329
|
+
},
|
|
330
|
+
},
|
|
295
331
|
};
|
|
296
332
|
}
|
|
297
333
|
|
|
298
334
|
if (lc === "on" || lc === "true" || lc === "1" || lc === "enable") {
|
|
299
335
|
return {
|
|
300
336
|
handled: true,
|
|
301
|
-
response: "
|
|
337
|
+
response: "",
|
|
302
338
|
settingsPatch: { visualPlanEnabled: true },
|
|
339
|
+
dialog: {
|
|
340
|
+
id: generateId(),
|
|
341
|
+
title: "Visual Plan",
|
|
342
|
+
command: "/visual-plan on",
|
|
343
|
+
component: "visual-plan",
|
|
344
|
+
data: {
|
|
345
|
+
enabled: true,
|
|
346
|
+
previousEnabled: currentEnabled,
|
|
347
|
+
mode: "toggle",
|
|
348
|
+
},
|
|
349
|
+
},
|
|
303
350
|
};
|
|
304
351
|
}
|
|
305
352
|
|
|
306
353
|
if (lc === "off" || lc === "false" || lc === "0" || lc === "disable") {
|
|
307
354
|
return {
|
|
308
355
|
handled: true,
|
|
309
|
-
response: "
|
|
356
|
+
response: "",
|
|
310
357
|
settingsPatch: { visualPlanEnabled: false },
|
|
358
|
+
dialog: {
|
|
359
|
+
id: generateId(),
|
|
360
|
+
title: "Visual Plan",
|
|
361
|
+
command: "/visual-plan off",
|
|
362
|
+
component: "visual-plan",
|
|
363
|
+
data: {
|
|
364
|
+
enabled: false,
|
|
365
|
+
previousEnabled: currentEnabled,
|
|
366
|
+
mode: "toggle",
|
|
367
|
+
},
|
|
368
|
+
},
|
|
311
369
|
};
|
|
312
370
|
}
|
|
313
371
|
|
|
314
372
|
if (lc === "status" || lc === "state" || lc === "?") {
|
|
315
|
-
const on = ctx.currentSettings.visualPlanEnabled ? "on" : "off";
|
|
316
373
|
return {
|
|
317
374
|
handled: true,
|
|
318
|
-
response:
|
|
375
|
+
response: "",
|
|
376
|
+
dialog: {
|
|
377
|
+
id: generateId(),
|
|
378
|
+
title: "Visual Plan",
|
|
379
|
+
command: "/visual-plan status",
|
|
380
|
+
component: "visual-plan",
|
|
381
|
+
data: {
|
|
382
|
+
enabled: currentEnabled,
|
|
383
|
+
previousEnabled: currentEnabled,
|
|
384
|
+
mode: "status",
|
|
385
|
+
},
|
|
386
|
+
},
|
|
319
387
|
};
|
|
320
388
|
}
|
|
321
389
|
|
|
@@ -376,21 +444,30 @@ function handlePlan(arg: string, ctx: ParseContext): SlashCommandResult {
|
|
|
376
444
|
function helpPlan(): SlashCommandResult {
|
|
377
445
|
return {
|
|
378
446
|
handled: true,
|
|
379
|
-
response:
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
447
|
+
response: "",
|
|
448
|
+
dialog: {
|
|
449
|
+
id: generateId(),
|
|
450
|
+
title: "Plan Commands",
|
|
451
|
+
command: "/plan",
|
|
452
|
+
component: "help",
|
|
453
|
+
data: {
|
|
454
|
+
commands: [
|
|
455
|
+
{ cmd: "/plan new <slug> [template]", desc: "Create a new plan" },
|
|
456
|
+
{ cmd: "/plan list", desc: "List all plans in the worktree" },
|
|
457
|
+
{ cmd: "/plan open <slug>", desc: "Open a plan in the viewer" },
|
|
458
|
+
{ cmd: "/plan get <slug>", desc: "Fetch the full canvas" },
|
|
459
|
+
{ cmd: "/plan add <slug> --title T --type kind", desc: "Add an element to a plan" },
|
|
460
|
+
{ cmd: "/plan update <slug> <id> [--x N --y N …]", desc: "Patch an existing element" },
|
|
461
|
+
{ cmd: "/plan delete <slug> <id>", desc: "Remove an element" },
|
|
462
|
+
{ cmd: "/plan comment <slug> [id] \"text\"", desc: "Add a comment" },
|
|
463
|
+
{ cmd: "/plan comments <slug> [id]", desc: "Read comments on a plan" },
|
|
464
|
+
{ cmd: "/plan status <slug> <status>", desc: "Set the plan's status" },
|
|
465
|
+
{ cmd: "/plan wait <slug> [--timeout N]", desc: "Wait for feedback (deferred)" },
|
|
466
|
+
],
|
|
467
|
+
templates: KNOWN_TEMPLATES,
|
|
468
|
+
statuses: PLAN_STATUSES,
|
|
469
|
+
},
|
|
470
|
+
},
|
|
394
471
|
};
|
|
395
472
|
}
|
|
396
473
|
|
|
@@ -400,7 +477,18 @@ function handlePlanNew(args: string[], ctx: ParseContext): SlashCommandResult {
|
|
|
400
477
|
if (args.length === 0 || args[0] === "") {
|
|
401
478
|
return {
|
|
402
479
|
handled: true,
|
|
403
|
-
response: "
|
|
480
|
+
response: "",
|
|
481
|
+
dialog: {
|
|
482
|
+
id: generateId(),
|
|
483
|
+
title: "Create New Plan",
|
|
484
|
+
command: "/plan new",
|
|
485
|
+
component: "plan-create",
|
|
486
|
+
data: {
|
|
487
|
+
templates: [...KNOWN_TEMPLATES],
|
|
488
|
+
defaultTemplate: ctx.currentSettings.defaultTemplate,
|
|
489
|
+
suggestedSlug: "",
|
|
490
|
+
},
|
|
491
|
+
},
|
|
404
492
|
};
|
|
405
493
|
}
|
|
406
494
|
|
|
@@ -428,24 +516,27 @@ function handlePlanNew(args: string[], ctx: ParseContext): SlashCommandResult {
|
|
|
428
516
|
template = candidate;
|
|
429
517
|
}
|
|
430
518
|
|
|
431
|
-
// The response surfaces the resolved template name (the user-supplied
|
|
432
|
-
// argument, or the user's current default). The sideEffect carries
|
|
433
|
-
// the explicit `null` so the executor knows to fall back to
|
|
434
|
-
// `currentSettings.defaultTemplate` at write time.
|
|
435
519
|
const resolvedTemplate = template ?? ctx.currentSettings.defaultTemplate;
|
|
436
520
|
|
|
437
521
|
return {
|
|
438
522
|
handled: true,
|
|
439
|
-
response:
|
|
440
|
-
`Plan "${titleCase(slug)}" (slug: ${slug}) will be created with the ` +
|
|
441
|
-
`"${resolvedTemplate}" template.\n` +
|
|
442
|
-
`After creation, use /plan open ${slug} to get the URL.`,
|
|
523
|
+
response: "",
|
|
443
524
|
sideEffect: {
|
|
444
525
|
kind: "create_plan",
|
|
445
526
|
slug,
|
|
446
527
|
template,
|
|
447
528
|
},
|
|
448
529
|
settingsPatch: { lastUsedSlug: slug },
|
|
530
|
+
dialog: {
|
|
531
|
+
id: generateId(),
|
|
532
|
+
title: "Plan Created",
|
|
533
|
+
command: `/plan new ${slug}`,
|
|
534
|
+
component: "generic",
|
|
535
|
+
data: {
|
|
536
|
+
message: `Plan "${titleCase(slug)}" created with the "${resolvedTemplate}" template.`,
|
|
537
|
+
detail: `Use /plan open ${slug} to open it.`,
|
|
538
|
+
},
|
|
539
|
+
},
|
|
449
540
|
};
|
|
450
541
|
}
|
|
451
542
|
|
|
@@ -453,19 +544,20 @@ function handlePlanNew(args: string[], ctx: ParseContext): SlashCommandResult {
|
|
|
453
544
|
|
|
454
545
|
function handlePlanList(ctx: ParseContext): SlashCommandResult {
|
|
455
546
|
const slugs = ctx.availablePlanSlugs ?? [];
|
|
456
|
-
if (slugs.length === 0) {
|
|
457
|
-
return {
|
|
458
|
-
handled: true,
|
|
459
|
-
response:
|
|
460
|
-
"No plans found in this worktree. Use /plan new <slug> to create one.",
|
|
461
|
-
sideEffect: { kind: "list_plans" },
|
|
462
|
-
};
|
|
463
|
-
}
|
|
464
|
-
const lines = slugs.map((s) => ` - ${s}`);
|
|
465
547
|
return {
|
|
466
548
|
handled: true,
|
|
467
|
-
response:
|
|
549
|
+
response: "",
|
|
468
550
|
sideEffect: { kind: "list_plans" },
|
|
551
|
+
dialog: {
|
|
552
|
+
id: generateId(),
|
|
553
|
+
title: "Plans",
|
|
554
|
+
command: "/plan list",
|
|
555
|
+
component: "plan-list",
|
|
556
|
+
data: {
|
|
557
|
+
plans: slugs,
|
|
558
|
+
count: slugs.length,
|
|
559
|
+
},
|
|
560
|
+
},
|
|
469
561
|
};
|
|
470
562
|
}
|
|
471
563
|
|
|
@@ -492,16 +584,22 @@ function handlePlanOpen(args: string[], ctx: ParseContext): SlashCommandResult {
|
|
|
492
584
|
|
|
493
585
|
return {
|
|
494
586
|
handled: true,
|
|
495
|
-
response:
|
|
496
|
-
`Plan URL: ${url}\n` +
|
|
497
|
-
`(v0.5.0 MVP — server startup is a future enhancement; the URL is ` +
|
|
498
|
-
`informational. Use "bizar plan open ${slug}" in the terminal to ` +
|
|
499
|
-
`start the local viewer.)`,
|
|
587
|
+
response: "",
|
|
500
588
|
settingsPatch: { lastUsedSlug: slug },
|
|
501
589
|
sideEffect: {
|
|
502
590
|
kind: "open_plan_url",
|
|
503
591
|
slug,
|
|
504
592
|
},
|
|
593
|
+
dialog: {
|
|
594
|
+
id: generateId(),
|
|
595
|
+
title: "Opening Plan",
|
|
596
|
+
command: `/plan open ${slug}`,
|
|
597
|
+
component: "generic",
|
|
598
|
+
data: {
|
|
599
|
+
message: `Opening plan: ${slug}`,
|
|
600
|
+
url,
|
|
601
|
+
},
|
|
602
|
+
},
|
|
505
603
|
};
|
|
506
604
|
}
|
|
507
605
|
|
|
@@ -520,13 +618,22 @@ function handlePlanGet(args: string[]): SlashCommandResult {
|
|
|
520
618
|
}
|
|
521
619
|
return {
|
|
522
620
|
handled: true,
|
|
523
|
-
response:
|
|
621
|
+
response: "",
|
|
524
622
|
sideEffect: {
|
|
525
623
|
kind: "tool_invocation",
|
|
526
624
|
toolName: "bizar_plan_action",
|
|
527
625
|
args: { action: "get_canvas", planSlug: slug },
|
|
528
626
|
},
|
|
529
627
|
settingsPatch: { lastUsedSlug: slug },
|
|
628
|
+
dialog: {
|
|
629
|
+
id: generateId(),
|
|
630
|
+
title: "Plan Canvas",
|
|
631
|
+
command: `/plan get ${slug}`,
|
|
632
|
+
component: "generic",
|
|
633
|
+
data: {
|
|
634
|
+
message: `Fetching canvas for plan "${slug}"…`,
|
|
635
|
+
},
|
|
636
|
+
},
|
|
530
637
|
};
|
|
531
638
|
}
|
|
532
639
|
|
|
@@ -585,13 +692,22 @@ function handlePlanAdd(args: string[]): SlashCommandResult {
|
|
|
585
692
|
}
|
|
586
693
|
return {
|
|
587
694
|
handled: true,
|
|
588
|
-
response:
|
|
695
|
+
response: "",
|
|
589
696
|
sideEffect: {
|
|
590
697
|
kind: "tool_invocation",
|
|
591
698
|
toolName: "bizar_plan_action",
|
|
592
699
|
args: { action: "add_element", planSlug: slug, element },
|
|
593
700
|
},
|
|
594
701
|
settingsPatch: { lastUsedSlug: slug },
|
|
702
|
+
dialog: {
|
|
703
|
+
id: generateId(),
|
|
704
|
+
title: "Element Added",
|
|
705
|
+
command: `/plan add ${slug}`,
|
|
706
|
+
component: "generic",
|
|
707
|
+
data: {
|
|
708
|
+
message: `Adding element to plan "${slug}"…`,
|
|
709
|
+
},
|
|
710
|
+
},
|
|
595
711
|
};
|
|
596
712
|
}
|
|
597
713
|
|
|
@@ -631,13 +747,22 @@ function handlePlanUpdate(args: string[]): SlashCommandResult {
|
|
|
631
747
|
}
|
|
632
748
|
return {
|
|
633
749
|
handled: true,
|
|
634
|
-
response:
|
|
750
|
+
response: "",
|
|
635
751
|
sideEffect: {
|
|
636
752
|
kind: "tool_invocation",
|
|
637
753
|
toolName: "bizar_plan_action",
|
|
638
754
|
args: { action: "update_element", planSlug: slug, elementId, element },
|
|
639
755
|
},
|
|
640
756
|
settingsPatch: { lastUsedSlug: slug },
|
|
757
|
+
dialog: {
|
|
758
|
+
id: generateId(),
|
|
759
|
+
title: "Element Updated",
|
|
760
|
+
command: `/plan update ${slug}`,
|
|
761
|
+
component: "generic",
|
|
762
|
+
data: {
|
|
763
|
+
message: `Updating element ${elementId} in plan "${slug}"…`,
|
|
764
|
+
},
|
|
765
|
+
},
|
|
641
766
|
};
|
|
642
767
|
}
|
|
643
768
|
|
|
@@ -660,13 +785,22 @@ function handlePlanDelete(args: string[]): SlashCommandResult {
|
|
|
660
785
|
}
|
|
661
786
|
return {
|
|
662
787
|
handled: true,
|
|
663
|
-
response:
|
|
788
|
+
response: "",
|
|
664
789
|
sideEffect: {
|
|
665
790
|
kind: "tool_invocation",
|
|
666
791
|
toolName: "bizar_plan_action",
|
|
667
792
|
args: { action: "delete_element", planSlug: slug, elementId },
|
|
668
793
|
},
|
|
669
794
|
settingsPatch: { lastUsedSlug: slug },
|
|
795
|
+
dialog: {
|
|
796
|
+
id: generateId(),
|
|
797
|
+
title: "Element Deleted",
|
|
798
|
+
command: `/plan delete ${slug}`,
|
|
799
|
+
component: "generic",
|
|
800
|
+
data: {
|
|
801
|
+
message: `Deleting element ${elementId} from plan "${slug}"…`,
|
|
802
|
+
},
|
|
803
|
+
},
|
|
670
804
|
};
|
|
671
805
|
}
|
|
672
806
|
|
|
@@ -728,10 +862,7 @@ function commentSideEffect(
|
|
|
728
862
|
): SlashCommandResult {
|
|
729
863
|
return {
|
|
730
864
|
handled: true,
|
|
731
|
-
response:
|
|
732
|
-
elementId === null
|
|
733
|
-
? `Adding canvas-pinned comment to plan "${slug}"…`
|
|
734
|
-
: `Adding comment to element ${elementId} on plan "${slug}"…`,
|
|
865
|
+
response: "",
|
|
735
866
|
sideEffect: {
|
|
736
867
|
kind: "tool_invocation",
|
|
737
868
|
toolName: "bizar_plan_action",
|
|
@@ -746,6 +877,18 @@ function commentSideEffect(
|
|
|
746
877
|
},
|
|
747
878
|
},
|
|
748
879
|
settingsPatch: { lastUsedSlug: slug },
|
|
880
|
+
dialog: {
|
|
881
|
+
id: generateId(),
|
|
882
|
+
title: "Comment Added",
|
|
883
|
+
command: `/plan comment ${slug}`,
|
|
884
|
+
component: "generic",
|
|
885
|
+
data: {
|
|
886
|
+
message:
|
|
887
|
+
elementId === null
|
|
888
|
+
? `Adding canvas-pinned comment to plan "${slug}"…`
|
|
889
|
+
: `Adding comment to element ${elementId} on plan "${slug}"…`,
|
|
890
|
+
},
|
|
891
|
+
},
|
|
749
892
|
};
|
|
750
893
|
}
|
|
751
894
|
|
|
@@ -765,16 +908,25 @@ function handlePlanComments(args: string[]): SlashCommandResult {
|
|
|
765
908
|
const elementId = args.length >= 2 ? args[1]! : undefined;
|
|
766
909
|
return {
|
|
767
910
|
handled: true,
|
|
768
|
-
response:
|
|
769
|
-
elementId === undefined
|
|
770
|
-
? `Reading comments on plan "${slug}"…`
|
|
771
|
-
: `Reading comments on element ${elementId} of plan "${slug}"…`,
|
|
911
|
+
response: "",
|
|
772
912
|
sideEffect: {
|
|
773
913
|
kind: "tool_invocation",
|
|
774
914
|
toolName: "bizar_get_plan_comments",
|
|
775
915
|
args: elementId === undefined ? { planSlug: slug } : { planSlug: slug, elementId },
|
|
776
916
|
},
|
|
777
917
|
settingsPatch: { lastUsedSlug: slug },
|
|
918
|
+
dialog: {
|
|
919
|
+
id: generateId(),
|
|
920
|
+
title: "Comments",
|
|
921
|
+
command: `/plan comments ${slug}`,
|
|
922
|
+
component: "generic",
|
|
923
|
+
data: {
|
|
924
|
+
message:
|
|
925
|
+
elementId === undefined
|
|
926
|
+
? `Reading comments on plan "${slug}"…`
|
|
927
|
+
: `Reading comments on element ${elementId} of plan "${slug}"…`,
|
|
928
|
+
},
|
|
929
|
+
},
|
|
778
930
|
};
|
|
779
931
|
}
|
|
780
932
|
|
|
@@ -803,13 +955,22 @@ function handlePlanStatus(args: string[]): SlashCommandResult {
|
|
|
803
955
|
}
|
|
804
956
|
return {
|
|
805
957
|
handled: true,
|
|
806
|
-
response:
|
|
958
|
+
response: "",
|
|
807
959
|
sideEffect: {
|
|
808
960
|
kind: "tool_invocation",
|
|
809
961
|
toolName: "bizar_plan_action",
|
|
810
962
|
args: { action: "set_status", planSlug: slug, status },
|
|
811
963
|
},
|
|
812
964
|
settingsPatch: { lastUsedSlug: slug },
|
|
965
|
+
dialog: {
|
|
966
|
+
id: generateId(),
|
|
967
|
+
title: "Status Updated",
|
|
968
|
+
command: `/plan status ${slug}`,
|
|
969
|
+
component: "generic",
|
|
970
|
+
data: {
|
|
971
|
+
message: `Setting plan "${slug}" status to "${status}"…`,
|
|
972
|
+
},
|
|
973
|
+
},
|
|
813
974
|
};
|
|
814
975
|
}
|
|
815
976
|
|
|
@@ -845,36 +1006,105 @@ function handlePlanWait(args: string[]): SlashCommandResult {
|
|
|
845
1006
|
};
|
|
846
1007
|
}
|
|
847
1008
|
|
|
1009
|
+
// --- /bizar --------------------------------------------------------------
|
|
1010
|
+
|
|
1011
|
+
/**
|
|
1012
|
+
* v2.5.0 — `/bizar [args]` launches the dashboard or routes a sub-request.
|
|
1013
|
+
*
|
|
1014
|
+
* Behavior:
|
|
1015
|
+
* - `/bizar` (no args) — emits a `launch_dashboard` side-effect. The
|
|
1016
|
+
* executor spawns `bizar dash start` as a detached child
|
|
1017
|
+
* process, then the host surfaces the URL in the response.
|
|
1018
|
+
* - `/bizar <args>` — passes the args to the menu command file. Today
|
|
1019
|
+
* the menu routes intent (`/explain`, `/plan`, `/audit`, etc.); the
|
|
1020
|
+
* response is the menu's natural-language routing advice.
|
|
1021
|
+
*
|
|
1022
|
+
* Note: the menu text lives in `config/commands/bizar.md` and is shipped
|
|
1023
|
+
* via the CLI package. The plugin only handles the no-arg case for the
|
|
1024
|
+
* side-effect; with args we return a short pointer so the user knows
|
|
1025
|
+
* where the routing table lives.
|
|
1026
|
+
*/
|
|
1027
|
+
function handleBizar(arg: string, ctx: ParseContext): SlashCommandResult {
|
|
1028
|
+
const trimmed = arg.trim();
|
|
1029
|
+
|
|
1030
|
+
if (trimmed === "") {
|
|
1031
|
+
const port = ctx.defaultPort ?? 4321;
|
|
1032
|
+
return {
|
|
1033
|
+
handled: true,
|
|
1034
|
+
response: "",
|
|
1035
|
+
sideEffect: {
|
|
1036
|
+
kind: "launch_dashboard",
|
|
1037
|
+
defaultPort: port,
|
|
1038
|
+
},
|
|
1039
|
+
dialog: {
|
|
1040
|
+
id: generateId(),
|
|
1041
|
+
title: "Dashboard",
|
|
1042
|
+
command: "/bizar",
|
|
1043
|
+
component: "generic",
|
|
1044
|
+
data: {
|
|
1045
|
+
message: "Dashboard launching in the background…",
|
|
1046
|
+
url: `http://localhost:${port}/`,
|
|
1047
|
+
},
|
|
1048
|
+
},
|
|
1049
|
+
};
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
// With args, defer to the menu command file shipped with the CLI.
|
|
1053
|
+
return {
|
|
1054
|
+
handled: true,
|
|
1055
|
+
response: "",
|
|
1056
|
+
dialog: {
|
|
1057
|
+
id: generateId(),
|
|
1058
|
+
title: "Bizar Commands",
|
|
1059
|
+
command: `/bizar ${trimmed}`,
|
|
1060
|
+
component: "help",
|
|
1061
|
+
data: {
|
|
1062
|
+
commands: [
|
|
1063
|
+
{ cmd: "/bizar", desc: "Launch the Bizar dashboard" },
|
|
1064
|
+
{ cmd: "/bizar explain <question>", desc: "Read-only code Q&A" },
|
|
1065
|
+
{ cmd: "/bizar plan <args>", desc: "Manage plans" },
|
|
1066
|
+
{ cmd: "/bizar audit", desc: "Run security audit" },
|
|
1067
|
+
{ cmd: "/bizar learn", desc: "Extract patterns from session" },
|
|
1068
|
+
{ cmd: "/bizar init", desc: "Initialize .bizar/ in this project" },
|
|
1069
|
+
{ cmd: "/bizar pr-review", desc: "PR review" },
|
|
1070
|
+
],
|
|
1071
|
+
},
|
|
1072
|
+
},
|
|
1073
|
+
};
|
|
1074
|
+
}
|
|
1075
|
+
|
|
848
1076
|
// --- /help ----------------------------------------------------------------
|
|
849
1077
|
|
|
850
1078
|
function helpResult(): SlashCommandResult {
|
|
851
1079
|
return {
|
|
852
1080
|
handled: true,
|
|
853
|
-
response:
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
1081
|
+
response: "",
|
|
1082
|
+
dialog: {
|
|
1083
|
+
id: generateId(),
|
|
1084
|
+
title: "Bizar Commands",
|
|
1085
|
+
command: "/help",
|
|
1086
|
+
component: "help",
|
|
1087
|
+
data: {
|
|
1088
|
+
commands: [
|
|
1089
|
+
{ cmd: "/visual-plan [on|off|status]", desc: "Toggle or view visual plan mode" },
|
|
1090
|
+
{ cmd: "/plan new <slug> [template]", desc: "Create a new plan" },
|
|
1091
|
+
{ cmd: "/plan list", desc: "List all plans in the worktree" },
|
|
1092
|
+
{ cmd: "/plan open <slug>", desc: "Open a plan in the viewer" },
|
|
1093
|
+
{ cmd: "/plan get <slug>", desc: "Fetch the full canvas" },
|
|
1094
|
+
{ cmd: "/plan add <slug> --title T --type kind", desc: "Add an element to a plan" },
|
|
1095
|
+
{ cmd: "/plan update <slug> <id> [flags]", desc: "Patch an existing element" },
|
|
1096
|
+
{ cmd: "/plan delete <slug> <id>", desc: "Remove an element" },
|
|
1097
|
+
{ cmd: "/plan comment <slug> [id] \"text\"", desc: "Add a comment" },
|
|
1098
|
+
{ cmd: "/plan comments <slug> [id]", desc: "Read comments on a plan" },
|
|
1099
|
+
{ cmd: "/plan status <slug> <status>", desc: "Set the plan's status" },
|
|
1100
|
+
{ cmd: "/plan wait <slug> [--timeout N]", desc: "Wait for feedback (deferred)" },
|
|
1101
|
+
{ cmd: "/bizar", desc: "Launch the Bizar dashboard" },
|
|
1102
|
+
{ cmd: "/bizar <args>", desc: "Route a request via the menu" },
|
|
1103
|
+
{ cmd: "/help | /commands", desc: "Show this help" },
|
|
1104
|
+
],
|
|
1105
|
+
templates: [...KNOWN_TEMPLATES],
|
|
1106
|
+
statuses: [...PLAN_STATUSES],
|
|
1107
|
+
},
|
|
1108
|
+
},
|
|
879
1109
|
};
|
|
880
1110
|
}
|
package/src/plan-fs.ts
CHANGED
|
@@ -43,7 +43,7 @@ import {
|
|
|
43
43
|
rmSync,
|
|
44
44
|
writeFileSync,
|
|
45
45
|
} from "node:fs";
|
|
46
|
-
import { join } from "node:path";
|
|
46
|
+
import { dirname, join } from "node:path";
|
|
47
47
|
|
|
48
48
|
import type { Logger } from "./logger.js";
|
|
49
49
|
|
|
@@ -146,7 +146,7 @@ function writeJsonAtomic(
|
|
|
146
146
|
): { ok: true } | { ok: false; error: string } {
|
|
147
147
|
const tmp = `${filePath}.tmp`;
|
|
148
148
|
try {
|
|
149
|
-
mkdirSync(
|
|
149
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
150
150
|
writeFileSync(tmp, JSON.stringify(data, null, 2), "utf-8");
|
|
151
151
|
renameSync(tmp, filePath);
|
|
152
152
|
return { ok: true };
|