@cmodjs/core 4.0.3 → 4.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.
Files changed (143) hide show
  1. package/docs/commands.md +44 -9
  2. package/docs/index.md +3 -3
  3. package/docs/install-steps.md +8 -8
  4. package/docs/jobs.md +2 -1
  5. package/docs/mod.md +1 -1
  6. package/docs/options.md +1 -1
  7. package/docs/permissions.md +1 -1
  8. package/docs/session.md +46 -13
  9. package/docs/testing.md +15 -11
  10. package/docs/ui.md +43 -11
  11. package/installer.d.ts +3 -2
  12. package/installer.d.ts.map +1 -1
  13. package/jobs/program.d.ts +1 -0
  14. package/jobs/program.d.ts.map +1 -1
  15. package/jobs/program.js +39 -23
  16. package/mod.d.ts +3 -2
  17. package/mod.d.ts.map +1 -1
  18. package/mod.js +1 -0
  19. package/package.json +1 -1
  20. package/records.d.ts +15 -1
  21. package/records.d.ts.map +1 -1
  22. package/records.js +39 -1
  23. package/register.d.ts.map +1 -1
  24. package/register.js +8 -5
  25. package/runtime/before.d.ts +6 -2
  26. package/runtime/before.d.ts.map +1 -1
  27. package/runtime/before.js +56 -18
  28. package/runtime/buttons.d.ts +10 -0
  29. package/runtime/buttons.d.ts.map +1 -0
  30. package/runtime/buttons.js +12 -0
  31. package/runtime/conversations.d.ts +3 -0
  32. package/runtime/conversations.d.ts.map +1 -1
  33. package/runtime/conversations.js +164 -27
  34. package/runtime/engine.d.ts +2 -0
  35. package/runtime/engine.d.ts.map +1 -1
  36. package/runtime/grants.d.ts +1 -0
  37. package/runtime/grants.d.ts.map +1 -1
  38. package/runtime/grants.js +5 -2
  39. package/runtime/hotkeys.d.ts.map +1 -1
  40. package/runtime/hotkeys.js +7 -4
  41. package/runtime/installer.d.ts +10 -6
  42. package/runtime/installer.d.ts.map +1 -1
  43. package/runtime/installer.js +106 -48
  44. package/runtime/lifecycle.d.ts +2 -2
  45. package/runtime/lifecycle.d.ts.map +1 -1
  46. package/runtime/lifecycle.js +144 -42
  47. package/runtime/pages.d.ts +3 -1
  48. package/runtime/pages.d.ts.map +1 -1
  49. package/runtime/pages.js +48 -14
  50. package/runtime/parts.d.ts +1 -0
  51. package/runtime/parts.d.ts.map +1 -1
  52. package/runtime/parts.js +6 -3
  53. package/runtime/programs.d.ts +1 -0
  54. package/runtime/programs.d.ts.map +1 -1
  55. package/runtime/programs.js +4 -1
  56. package/runtime/sessions.d.ts +10 -3
  57. package/runtime/sessions.d.ts.map +1 -1
  58. package/runtime/sessions.js +104 -59
  59. package/runtime/toaster.d.ts.map +1 -1
  60. package/runtime/toaster.js +23 -9
  61. package/runtime/ui.d.ts +22 -4
  62. package/runtime/ui.d.ts.map +1 -1
  63. package/runtime/ui.js +161 -59
  64. package/runtime/watches.d.ts.map +1 -1
  65. package/runtime/watches.js +18 -4
  66. package/runtime/widgets.d.ts +7 -5
  67. package/runtime/widgets.d.ts.map +1 -1
  68. package/runtime/widgets.js +31 -21
  69. package/session/context/turn/message/attached-file.d.ts +13 -0
  70. package/session/context/turn/message/attached-file.d.ts.map +1 -0
  71. package/session/context/turn/message/attached-file.js +21 -0
  72. package/session/context/turn/message/message.d.ts +3 -2
  73. package/session/context/turn/message/message.d.ts.map +1 -1
  74. package/session/context/turn/message/message.js +20 -6
  75. package/session/conversation.d.ts +15 -3
  76. package/session/conversation.d.ts.map +1 -1
  77. package/session/conversation.js +23 -4
  78. package/session/goal/task.d.ts +5 -0
  79. package/session/goal/task.d.ts.map +1 -1
  80. package/session/session.d.ts +36 -7
  81. package/session/session.d.ts.map +1 -1
  82. package/session/session.js +2 -1
  83. package/src/installer.ts +3 -2
  84. package/src/jobs/program.ts +41 -21
  85. package/src/mod.ts +3 -2
  86. package/src/records.ts +49 -1
  87. package/src/register.ts +11 -8
  88. package/src/runtime/before.ts +61 -22
  89. package/src/runtime/buttons.ts +22 -0
  90. package/src/runtime/conversations.ts +170 -34
  91. package/src/runtime/engine.ts +2 -0
  92. package/src/runtime/grants.ts +6 -2
  93. package/src/runtime/hotkeys.ts +7 -4
  94. package/src/runtime/installer.ts +147 -83
  95. package/src/runtime/lifecycle.ts +149 -48
  96. package/src/runtime/pages.ts +56 -12
  97. package/src/runtime/parts.ts +8 -4
  98. package/src/runtime/programs.ts +5 -1
  99. package/src/runtime/sessions.ts +106 -59
  100. package/src/runtime/toaster.ts +27 -9
  101. package/src/runtime/ui.ts +166 -62
  102. package/src/runtime/watches.ts +20 -5
  103. package/src/runtime/widgets.ts +35 -23
  104. package/src/session/context/turn/message/attached-file.ts +36 -0
  105. package/src/session/context/turn/message/message.ts +20 -7
  106. package/src/session/conversation.ts +31 -6
  107. package/src/session/goal/task.ts +6 -0
  108. package/src/session/session.ts +18 -7
  109. package/src/testing/fake-claude.ts +21 -6
  110. package/src/testing/fake-elements.ts +57 -8
  111. package/src/testing/fake-sessions.ts +72 -24
  112. package/src/testing.ts +114 -33
  113. package/src/ui/areas.ts +1 -0
  114. package/src/ui/components.ts +14 -8
  115. package/src/ui/elements.ts +6 -2
  116. package/src/ui/markdown.ts +3 -3
  117. package/src/ui/panes.ts +2 -2
  118. package/src/ui/styles.ts +6 -2
  119. package/testing/fake-claude.d.ts +3 -0
  120. package/testing/fake-claude.d.ts.map +1 -1
  121. package/testing/fake-claude.js +18 -5
  122. package/testing/fake-elements.d.ts.map +1 -1
  123. package/testing/fake-elements.js +52 -7
  124. package/testing/fake-sessions.d.ts +24 -10
  125. package/testing/fake-sessions.d.ts.map +1 -1
  126. package/testing/fake-sessions.js +66 -20
  127. package/testing.d.ts +8 -12
  128. package/testing.d.ts.map +1 -1
  129. package/testing.js +113 -27
  130. package/ui/areas.d.ts +4 -0
  131. package/ui/areas.d.ts.map +1 -1
  132. package/ui/components.d.ts +1 -0
  133. package/ui/components.d.ts.map +1 -1
  134. package/ui/components.js +11 -7
  135. package/ui/elements.d.ts +3 -0
  136. package/ui/elements.d.ts.map +1 -1
  137. package/ui/elements.js +6 -1
  138. package/ui/markdown.d.ts +2 -2
  139. package/ui/markdown.d.ts.map +1 -1
  140. package/ui/panes.d.ts +2 -1
  141. package/ui/panes.d.ts.map +1 -1
  142. package/ui/styles.d.ts +11 -2
  143. package/ui/styles.d.ts.map +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"hotkeys.d.ts","sourceRoot":"","sources":["../src/runtime/hotkeys.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAIpD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAE3C,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAS7G;AAED,wBAAsB,aAAa,CAAC,EAClC,IAAI,EACJ,QAAQ,EACR,UAAU,EACV,MAAM,EACN,MAAM,EACN,WAAW,EACX,MAAM,GACP,EAAE;IACD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAA;IACvC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,WAAW,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;IAC7G,QAAQ,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;CAC5C,GAAG,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CA4C5C"}
1
+ {"version":3,"file":"hotkeys.d.ts","sourceRoot":"","sources":["../src/runtime/hotkeys.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAIpD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEzC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAE3C,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAS7G;AAED,wBAAsB,aAAa,CAAC,EAClC,IAAI,EACJ,QAAQ,EACR,UAAU,EACV,MAAM,EACN,MAAM,EACN,WAAW,EACX,MAAM,GACP,EAAE;IACD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAA;IACvC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,WAAW,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;IAC7G,QAAQ,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;CAC5C,GAAG,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CA8C5C"}
@@ -1,6 +1,7 @@
1
1
  import { boundCombos, hotkeyCommand } from '../records.js';
2
2
  import { ifMissing } from '../utils/paths.js';
3
3
  import { messageOf } from '../utils/text.js';
4
+ import { cmodPluginName } from './dependencies.js';
4
5
  export async function readCombos(claude, configRoot) {
5
6
  if (configRoot === undefined)
6
7
  return new Map();
@@ -14,6 +15,12 @@ export async function readCombos(claude, configRoot) {
14
15
  }
15
16
  }
16
17
  export async function createHotkeys({ name, declared, configRoot, claude, router, answerInput, report, }) {
18
+ if (name === cmodPluginName) {
19
+ router.add('command.describe', async (e, next) => {
20
+ const described = await next(e);
21
+ return e.command.startsWith(hotkeyCommand(e.provider.plugin.replace(/@[^@]*$/, ''), '')) ? { ...described, isHidden: true } : described;
22
+ });
23
+ }
17
24
  const entries = Object.entries(declared);
18
25
  if (entries.length === 0)
19
26
  return {};
@@ -24,10 +31,6 @@ export async function createHotkeys({ name, declared, configRoot, claude, router
24
31
  listeners.set(hotkey, new Set());
25
32
  claude.command.register({ name: hotkeyCommand(name, hotkey), description: title, immediate: true }).then(({ command }) => void commands.set(command, hotkey), (error) => report({ id: `hotkey:${hotkey}`, message: `The ${title} hotkey is not added`, error }));
26
33
  }
27
- router.add('command.describe', async (e, next) => {
28
- const described = await next(e);
29
- return commands.has(e.command) ? { ...described, isHidden: true } : described;
30
- });
31
34
  router.add('command.run', async (e, next) => {
32
35
  const hotkey = commands.get(e.command);
33
36
  if (hotkey === undefined)
@@ -1,27 +1,31 @@
1
- import type { Args, Frozen, RenderElement } from 'claude-code';
2
1
  import type { Step } from '../installer.js';
3
2
  import type { Mod } from '../mod.js';
4
3
  import type { Option } from '../options.js';
5
4
  import type { Claude } from './claude.js';
6
- export declare const installerPaneOf: (name: string) => string;
5
+ import { type PartsUpdate, type ShownPages } from './ui.js';
6
+ export declare const installPage = "cmod:install";
7
7
  export type Consent = {
8
8
  readonly install: string;
9
9
  readonly uninstall: string;
10
+ readonly program: string;
10
11
  readonly hotkeys: string;
11
12
  readonly permissions: readonly string[];
12
13
  readonly updates?: string | undefined;
13
14
  };
14
15
  export type Installer = {
15
- draws(e: Frozen<Args<'ui.render'>>): boolean;
16
- draw(e: Frozen<Args<'ui.render'>>): RenderElement;
17
- closed(id: string): void;
16
+ answer(method: string, input: unknown): Promise<{
17
+ readonly value: unknown;
18
+ }> | undefined;
19
+ hear(shown: ShownPages): void;
18
20
  consent(consent: Consent): Promise<boolean>;
19
21
  options(missing: readonly (readonly [string, Option])[], save: (key: string, value: string) => Promise<string | undefined>): Promise<boolean>;
20
22
  step<State extends object>(mod: Mod<State>, step: Step<State>, index: number, total: number): Promise<boolean>;
21
23
  close(): Promise<void>;
24
+ shown(): Promise<void>;
25
+ collect(run: () => Promise<unknown>): Promise<PartsUpdate>;
22
26
  };
23
27
  export declare function createInstaller(name: string, claude: () => Claude, waiting: {
24
28
  show(open: () => Promise<void>): void;
25
29
  hide(): void;
26
- }): Installer;
30
+ }, nextDraw: () => number): Installer;
27
31
  //# sourceMappingURL=installer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"installer.d.ts","sourceRoot":"","sources":["../src/runtime/installer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,aAAa,EAAiB,MAAM,aAAa,CAAA;AAC7E,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AACpC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AAI3C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEzC,eAAO,MAAM,eAAe,SAAU,MAAM,KAAG,MAA0E,CAAA;AAEzH,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAA;IACvC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACtC,CAAA;AAOD,MAAM,MAAM,SAAS,GAAG;IACtB,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,GAAG,OAAO,CAAA;IAC5C,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,GAAG,aAAa,CAAA;IACjD,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC3C,OAAO,CAAC,OAAO,EAAE,SAAS,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC7I,IAAI,CAAC,KAAK,SAAS,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC9G,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACvB,CAAA;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,MAAM,EAAE,OAAO,EAAE;IAAE,IAAI,CAAC,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAAC,IAAI,IAAI,IAAI,CAAA;CAAE,GAAG,SAAS,CAoJ/I"}
1
+ {"version":3,"file":"installer.d.ts","sourceRoot":"","sources":["../src/runtime/installer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAA;AACpC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AAM3C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEzC,OAAO,EAAmE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,SAAS,CAAA;AAE5H,eAAO,MAAM,WAAW,iBAAiB,CAAA;AAEzC,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAA;IACvC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACtC,CAAA;AAOD,MAAM,MAAM,SAAS,GAAG;IACtB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC;QAAE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,GAAG,SAAS,CAAA;IACxF,IAAI,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAA;IAC7B,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC3C,OAAO,CAAC,OAAO,EAAE,SAAS,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC7I,IAAI,CAAC,KAAK,SAAS,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC9G,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACtB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACtB,OAAO,CAAC,GAAG,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,CAAA;CAC3D,CAAA;AAID,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,MAAM,EAAE,OAAO,EAAE;IAAE,IAAI,CAAC,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAAC,IAAI,IAAI,IAAI,CAAA;CAAE,EAAE,QAAQ,EAAE,MAAM,MAAM,GAAG,SAAS,CA4MvK"}
@@ -1,75 +1,129 @@
1
- import { permissionWords, secretFix } from '../records.js';
2
- import { Box, Button, drawWith, Input, Text } from '../ui/elements.js';
1
+ import { isObject, permissionWords, secretFix } from '../records.js';
2
+ import { Box, elementData, Input, Text } from '../ui/elements.js';
3
+ import { filledButton } from './buttons.js';
3
4
  import { messageOf } from '../utils/text.js';
4
- export const installerPaneOf = (name) => `installer-${name.replace(/[^A-Za-z0-9_-]/g, '_')}`.slice(0, 64);
5
- export function createInstaller(name, claude, waiting) {
6
- const paneId = installerPaneOf(name);
7
- let page;
5
+ import { cmodPluginName, withBareRefusals } from './dependencies.js';
6
+ import { merged, partMethods, stalePress } from './ui.js';
7
+ export const installPage = 'cmod:install';
8
+ const keptDraws = 8;
9
+ export function createInstaller(name, claude, waiting, nextDraw) {
10
+ let question;
8
11
  let isOpen = false;
9
- const open = async () => {
10
- isOpen = true;
11
- await claude().ui.open({ id: paneId, title: `Install ${name}`, focus: true, holdToasts: true, closeOnEscape: true });
12
+ let isSeen = false;
13
+ let waitingForShow = [];
14
+ let collected;
15
+ const draws = new Map();
16
+ const send = async (update) => {
17
+ if (collected !== undefined)
18
+ return void collected.push(update);
19
+ await withBareRefusals(claude()).cmod.call({ to: cmodPluginName, method: partMethods.changed, input: update });
12
20
  };
21
+ const log = (what) => (error) => claude().ui.log(`${name}: ${what}: ${messageOf(error)}`, { to: 'debug' });
22
+ const open = (isAsked) => send({ requests: [{ open: installPage, focus: true }], ...(isAsked ? { isAsked: true } : {}) });
13
23
  const show = async (build, cancelled) => {
14
- page?.cancel();
24
+ question?.cancel();
15
25
  const answer = new Promise((resolve) => {
26
+ let isAnswered = false;
16
27
  const done = (value) => {
17
- if (page === shown)
18
- page = undefined;
28
+ if (isAnswered)
29
+ return;
30
+ isAnswered = true;
19
31
  waiting.hide();
20
- resolve(value);
32
+ claude().clock.after(0, () => resolve(value));
21
33
  };
22
- const shown = { ...build(done), cancel: () => done(cancelled) };
23
- page = shown;
34
+ question = { ...build(done), cancel: () => done(cancelled) };
24
35
  });
25
- waiting.show(open);
26
- if (!isOpen)
27
- await open().catch((error) => {
36
+ waiting.show(() => open(true));
37
+ if (isOpen) {
38
+ await send({ redraw: true }).catch(log('the installer did not redraw'));
39
+ }
40
+ else {
41
+ isOpen = true;
42
+ await open(false).catch((error) => {
43
+ isOpen = false;
28
44
  waiting.hide();
29
45
  throw error;
30
46
  });
31
- claude().ui.invalidate('ui.render');
47
+ }
48
+ for (const resolve of waitingForShow)
49
+ resolve();
50
+ waitingForShow = [];
32
51
  return answer;
33
52
  };
34
- const frame = (title, body, actions) => Box({ flexDirection: 'column', gap: 1, paddingX: 1, children: [Text({ bold: true, children: title }), ...body, Box({ gap: 3, children: actions })] });
35
- const bullet = (text) => Text({ children: [Text({ color: 'claude', children: '◆ ' }), text] });
53
+ const redraw = () => void send({ redraw: true }).catch(log('the installer did not redraw'));
54
+ const actions = (...buttons) => Box({ gap: 2, flexShrink: 0, children: buttons });
55
+ const dialog = (title, ...children) => Box({ flexDirection: 'column', gap: 1, children: [title, ...children] });
56
+ const bullet = (text) => Box({ gap: 1, children: [Text({ dimColor: true, children: '•' }), Text({ wrap: 'wrap', children: text })] });
57
+ const group = (heading, items) => Box({ flexDirection: 'column', children: [Text({ bold: true, children: heading }), ...items.map(bullet)] });
58
+ const draw = (columns, isFocused) => {
59
+ if (question === undefined && !isOpen)
60
+ claude().clock.after(0, () => void send({ requests: [{ close: installPage }] }).catch(log('a leftover install page stayed open')));
61
+ const handlers = [];
62
+ const tree = elementData(question?.draw({ params: undefined, isFocused, columns }) ?? Text({ dimColor: true, children: `${name} needs nothing from you now.` }), handlers);
63
+ const drawn = nextDraw();
64
+ draws.set(drawn, handlers);
65
+ for (const kept of draws.keys())
66
+ if (draws.size > keptDraws)
67
+ draws.delete(kept);
68
+ return { draw: drawn, tree };
69
+ };
70
+ const collect = async (run) => {
71
+ const outer = collected;
72
+ const mine = [];
73
+ collected = mine;
74
+ try {
75
+ await run();
76
+ }
77
+ finally {
78
+ collected = outer;
79
+ }
80
+ return merged(mine);
81
+ };
82
+ const press = async ({ draw: drawn, handler, args }) => {
83
+ const run = draws.get(drawn)?.[handler];
84
+ if (run === undefined)
85
+ throw new Error(`${name}: ${stalePress}`);
86
+ return collect(() => run(...args));
87
+ };
36
88
  return {
37
- draws: (e) => e.component === 'Pane' && e.requestId === paneId,
38
- draw(e) {
39
- return drawWith(claude().ui.resolve(e), () => page?.draw(e.props) ?? Text({ dimColor: true, children: `${name} is installing.` }));
89
+ answer(method, input) {
90
+ if (method === partMethods.draw && isObject(input) && input['page'] === installPage)
91
+ return Promise.resolve({ value: draw(Number(input['columns']), input['isFocused'] === true) });
92
+ if (method === partMethods.press && isObject(input) && typeof input['draw'] === 'number' && draws.has(input['draw']))
93
+ return press(input).then((value) => ({ value }));
94
+ return undefined;
40
95
  },
41
- closed(id) {
42
- if (id !== paneId)
96
+ hear(shown) {
97
+ if (shown.open.some((each) => each.id === installPage)) {
98
+ isSeen = isOpen;
99
+ return;
100
+ }
101
+ if (!isSeen)
43
102
  return;
44
103
  isOpen = false;
45
- page?.cancel();
104
+ isSeen = false;
105
+ question?.cancel();
46
106
  },
47
- consent({ install, uninstall, hotkeys, permissions, updates }) {
107
+ consent({ install, uninstall, program, hotkeys, permissions, updates }) {
48
108
  const changes = [
49
109
  ...(install === '' ? [] : [`Run ${install} now${uninstall === '' ? '' : `, and ${uninstall} when you remove it`}`]),
50
110
  ...(install === '' && uninstall !== '' ? [`Run ${uninstall} when you remove it`] : []),
111
+ ...(program === '' ? [] : [`Install ${program}, which Claude runs by its name`]),
51
112
  ...(hotkeys === '' ? [] : [`Bind ${hotkeys}`]),
52
113
  ...(updates === undefined ? [] : [`Update ${name} automatically from the ${updates} marketplace`]),
53
114
  ];
54
115
  return show((answer) => ({
55
- draw: () => frame(`${name} wants to:`, [
56
- ...(permissions.length === 0 ? [] : [Box({ flexDirection: 'column', children: permissions.map((item) => bullet(permissionWords(item))) })]),
57
- ...(changes.length === 0 ? [] : [Text({ children: permissions.length === 0 ? 'Change your computer:' : 'and change your computer:' }), Box({ flexDirection: 'column', children: changes.map(bullet) })]),
58
- Text({ dimColor: true, children: 'You can turn each permission off later in Settings.' }),
59
- ], [Button({ key: 'not-now', hotkey: 'n', label: 'Not now', onPress: () => answer(false) }), Button({ key: 'accept', hotkey: 'a', label: 'Accept', onPress: () => answer(true) })]),
116
+ draw: () => dialog(Text({ bold: true, children: `Install ${name}?` }), ...(permissions.length === 0 ? [] : [group(`${name} wants to:`, permissions.map(permissionWords))]), ...(changes.length === 0 ? [] : [group(permissions.length === 0 ? `${name} wants to change your computer:` : 'and change your computer:', changes)]), Text({ dimColor: true, wrap: 'wrap', children: 'You can turn each permission off later in Settings.' }), actions(filledButton({ key: 'accept', hotkey: 'a', variant: 'primary', label: 'Accept', onPress: () => answer(true) }), filledButton({ key: 'not-now', hotkey: 'n', label: 'Not now', onPress: () => answer(false) }))),
60
117
  }), false);
61
118
  },
62
119
  options(missing, save) {
63
120
  const saved = new Map();
64
121
  const failures = new Map();
65
122
  return show((answer) => ({
66
- draw: () => frame(`${name} needs ${missing.length === 1 ? 'one setting' : `${missing.length} settings`}:`, missing.map(([key, option]) => {
67
- const label = Text({ children: [Text({ bold: true, children: option.title }), option.description === '' ? '' : Text({ dimColor: true, children: ` ${option.description}` })] });
123
+ draw: () => dialog(Text({ bold: true, children: `Set up ${name}` }), Text({ children: `${name} needs ${missing.length === 1 ? 'one setting' : `${missing.length} settings`}:` }), ...missing.map(([key, option]) => {
124
+ const label = Box({ flexDirection: 'column', children: [Text({ bold: true, children: option.title }), ...(option.description === '' ? [] : [Text({ dimColor: true, wrap: 'wrap', children: option.description })])] });
68
125
  if (option.type === 'secret') {
69
- return Box({
70
- flexDirection: 'column',
71
- children: [label, Text({ dimColor: true, children: `${secretFix} Then press Next.` }), Button({ key: `secret:${key}`, label: 'Open /config', onPress: () => void claude().command.run({ command: 'config' }) })],
72
- });
126
+ return Box({ flexDirection: 'column', children: [label, Text({ dimColor: true, wrap: 'wrap', children: `${secretFix} Then press Next.` }), filledButton({ key: `secret:${key}`, label: 'Open /config', onPress: () => void claude().command.run({ command: 'config' }) })] });
73
127
  }
74
128
  const failure = failures.get(key);
75
129
  const field = Input({
@@ -88,11 +142,11 @@ export function createInstaller(name, claude, waiting) {
88
142
  failures.set(key, refused);
89
143
  })
90
144
  .catch((error) => failures.set(key, messageOf(error)))
91
- .finally(() => claude().ui.invalidate('ui.render')),
145
+ .finally(redraw),
92
146
  });
93
- const status = failure !== undefined ? Text({ color: 'error', children: failure }) : saved.has(key) ? Text({ color: 'success', children: '✔ saved' }) : Text({ children: '' });
94
- return Box({ flexDirection: 'column', children: [label, field, status] });
95
- }), [Button({ key: 'not-now', hotkey: 'n', label: 'Not now', onPress: () => answer(false) }), Button({ key: 'next', hotkey: 'x', label: 'Next', onPress: () => answer(true) })]),
147
+ const status = failure !== undefined ? [Text({ color: 'error', wrap: 'wrap', children: failure })] : saved.has(key) ? [Text({ color: 'success', children: '✓ saved' })] : [];
148
+ return Box({ flexDirection: 'column', children: [label, field, ...status] });
149
+ }), actions(filledButton({ key: 'next', hotkey: 'x', variant: 'primary', label: 'Next', onPress: () => answer(true) }), filledButton({ key: 'not-now', hotkey: 'n', label: 'Not now', onPress: () => answer(false) }))),
96
150
  }), false);
97
151
  },
98
152
  step(mod, step, index, total) {
@@ -104,7 +158,7 @@ export function createInstaller(name, claude, waiting) {
104
158
  body = step.render(mod, props);
105
159
  }
106
160
  catch (error) {
107
- body = Text({ color: 'error', children: `The ${step.title} step could not draw: ${messageOf(error)}` });
161
+ body = Text({ color: 'error', wrap: 'wrap', children: `The ${step.title} step could not draw: ${messageOf(error)}` });
108
162
  }
109
163
  const next = async () => {
110
164
  try {
@@ -115,18 +169,22 @@ export function createInstaller(name, claude, waiting) {
115
169
  catch (error) {
116
170
  notice = { color: 'error', text: `The ${step.title} step's isDone failed: ${messageOf(error)}` };
117
171
  }
118
- claude().ui.invalidate('ui.render');
172
+ await send({ redraw: true });
119
173
  };
120
- return frame(`${step.title} (${index} of ${total})`, [body, ...(notice === undefined ? [] : [Text({ color: notice.color, children: notice.text })])], [Button({ key: 'not-now', hotkey: 'n', label: 'Not now', onPress: () => answer(false) }), Button({ key: 'next', hotkey: 'x', label: index === total ? 'Finish' : 'Next', onPress: () => void next() })]);
174
+ return dialog(Text({ children: [Text({ bold: true, children: step.title }), Text({ dimColor: true, children: ` (${index} of ${total})` })] }), body, ...(notice === undefined ? [] : [Text({ color: notice.color, wrap: 'wrap', children: notice.text })]), actions(filledButton({ key: 'next', hotkey: 'x', variant: 'primary', label: index === total ? 'Finish' : 'Next', onPress: () => void next() }), filledButton({ key: 'not-now', hotkey: 'n', label: 'Not now', onPress: () => answer(false) })));
121
175
  },
122
176
  }), false);
123
177
  },
124
178
  async close() {
125
- page = undefined;
179
+ question?.cancel();
180
+ question = undefined;
126
181
  if (!isOpen)
127
182
  return;
128
183
  isOpen = false;
129
- await claude().ui.close({ id: paneId });
184
+ isSeen = false;
185
+ await send({ requests: [{ close: installPage }] }).catch(log('the install page stayed open'));
130
186
  },
187
+ shown: () => new Promise((resolve) => void waitingForShow.push(resolve)),
188
+ collect,
131
189
  };
132
190
  }
@@ -1,4 +1,4 @@
1
- import type { Args, EventResult, Frozen, HookFailure, PluginOptions } from 'claude-code';
1
+ import type { Args, Caught, EventResult, Frozen, PluginOptions } from 'claude-code';
2
2
  import type { Mod, ModDefinition } from '../mod.js';
3
3
  import type { Options } from '../options.js';
4
4
  import { type Steps } from '../records.js';
@@ -25,7 +25,7 @@ export type Lifecycle<State extends object> = {
25
25
  readonly toasts: readonly ShownToast[];
26
26
  start(claude: Claude, read: (claude: Claude) => Promise<Plugin>): Promise<void>;
27
27
  route<N extends RoutedEvent>(event: N, e: Frozen<Args<N>>, next: RouterNext<N>): Promise<EventResult<N>>;
28
- caught<N extends RoutedEvent>(event: N, e: Frozen<Args<N>>, next: RouterNext<N>, failure: HookFailure): Promise<EventResult<N>>;
28
+ caught<N extends RoutedEvent>(event: N, e: Frozen<Args<N>>, next: RouterNext<N>, failure: Caught): Promise<EventResult<N>>;
29
29
  raise(method: string, payload: Readonly<Record<string, unknown>>): Promise<unknown>;
30
30
  };
31
31
  export declare function readPlugin(claude: Claude): Promise<Plugin>;
@@ -1 +1 @@
1
- {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../src/runtime/lifecycle.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../src/runtime/lifecycle.ts"],"names":[],"mappings":"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"}