reviewflow 3.28.0 → 3.29.0

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 (123) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/dashboard/index.html +61 -0
  3. package/dist/dashboard/modules/emberChat.d.ts +77 -0
  4. package/dist/dashboard/modules/emberChat.d.ts.map +1 -0
  5. package/dist/dashboard/modules/emberChat.js +175 -0
  6. package/dist/dashboard/modules/emberChat.js.map +1 -0
  7. package/dist/dashboard/styles.css +85 -0
  8. package/dist/main/routes.d.ts.map +1 -1
  9. package/dist/main/routes.js +32 -0
  10. package/dist/main/routes.js.map +1 -1
  11. package/dist/modules/ember-chat/entities/emberMessage/emberMessage.guard.d.ts +4 -0
  12. package/dist/modules/ember-chat/entities/emberMessage/emberMessage.guard.d.ts.map +1 -0
  13. package/dist/modules/ember-chat/entities/emberMessage/emberMessage.guard.js +4 -0
  14. package/dist/modules/ember-chat/entities/emberMessage/emberMessage.guard.js.map +1 -0
  15. package/dist/modules/ember-chat/entities/emberMessage/emberMessage.schema.d.ts +6 -0
  16. package/dist/modules/ember-chat/entities/emberMessage/emberMessage.schema.d.ts.map +1 -0
  17. package/dist/modules/ember-chat/entities/emberMessage/emberMessage.schema.js +5 -0
  18. package/dist/modules/ember-chat/entities/emberMessage/emberMessage.schema.js.map +1 -0
  19. package/dist/modules/ember-chat/entities/emberSession/emberSession.schema.d.ts +7 -0
  20. package/dist/modules/ember-chat/entities/emberSession/emberSession.schema.d.ts.map +1 -0
  21. package/dist/modules/ember-chat/entities/emberSession/emberSession.schema.js +3 -0
  22. package/dist/modules/ember-chat/entities/emberSession/emberSession.schema.js.map +1 -0
  23. package/dist/modules/ember-chat/entities/emberSession/emberSessionState.d.ts +13 -0
  24. package/dist/modules/ember-chat/entities/emberSession/emberSessionState.d.ts.map +1 -0
  25. package/dist/modules/ember-chat/entities/emberSession/emberSessionState.js +35 -0
  26. package/dist/modules/ember-chat/entities/emberSession/emberSessionState.js.map +1 -0
  27. package/dist/modules/ember-chat/entities/emberSession/emberSessionTransport.gateway.d.ts +26 -0
  28. package/dist/modules/ember-chat/entities/emberSession/emberSessionTransport.gateway.d.ts.map +1 -0
  29. package/dist/modules/ember-chat/entities/emberSession/emberSessionTransport.gateway.js +2 -0
  30. package/dist/modules/ember-chat/entities/emberSession/emberSessionTransport.gateway.js.map +1 -0
  31. package/dist/modules/ember-chat/entities/emberTool/emberTool.gateway.d.ts +11 -0
  32. package/dist/modules/ember-chat/entities/emberTool/emberTool.gateway.d.ts.map +1 -0
  33. package/dist/modules/ember-chat/entities/emberTool/emberTool.gateway.js +2 -0
  34. package/dist/modules/ember-chat/entities/emberTool/emberTool.gateway.js.map +1 -0
  35. package/dist/modules/ember-chat/interface-adapters/controllers/http/emberChat.routes.d.ts +15 -0
  36. package/dist/modules/ember-chat/interface-adapters/controllers/http/emberChat.routes.d.ts.map +1 -0
  37. package/dist/modules/ember-chat/interface-adapters/controllers/http/emberChat.routes.js +61 -0
  38. package/dist/modules/ember-chat/interface-adapters/controllers/http/emberChat.routes.js.map +1 -0
  39. package/dist/modules/ember-chat/interface-adapters/gateways/emberReadData.composite.gateway.d.ts +24 -0
  40. package/dist/modules/ember-chat/interface-adapters/gateways/emberReadData.composite.gateway.d.ts.map +1 -0
  41. package/dist/modules/ember-chat/interface-adapters/gateways/emberReadData.composite.gateway.js +19 -0
  42. package/dist/modules/ember-chat/interface-adapters/gateways/emberReadData.composite.gateway.js.map +1 -0
  43. package/dist/modules/ember-chat/interface-adapters/gateways/emberSessionTransport.claude.gateway.d.ts +13 -0
  44. package/dist/modules/ember-chat/interface-adapters/gateways/emberSessionTransport.claude.gateway.d.ts.map +1 -0
  45. package/dist/modules/ember-chat/interface-adapters/gateways/emberSessionTransport.claude.gateway.js +130 -0
  46. package/dist/modules/ember-chat/interface-adapters/gateways/emberSessionTransport.claude.gateway.js.map +1 -0
  47. package/dist/modules/ember-chat/interface-adapters/gateways/emberStreamJson.parser.d.ts +18 -0
  48. package/dist/modules/ember-chat/interface-adapters/gateways/emberStreamJson.parser.d.ts.map +1 -0
  49. package/dist/modules/ember-chat/interface-adapters/gateways/emberStreamJson.parser.js +33 -0
  50. package/dist/modules/ember-chat/interface-adapters/gateways/emberStreamJson.parser.js.map +1 -0
  51. package/dist/modules/ember-chat/interface-adapters/presenters/emberStatus.presenter.d.ts +20 -0
  52. package/dist/modules/ember-chat/interface-adapters/presenters/emberStatus.presenter.d.ts.map +1 -0
  53. package/dist/modules/ember-chat/interface-adapters/presenters/emberStatus.presenter.js +28 -0
  54. package/dist/modules/ember-chat/interface-adapters/presenters/emberStatus.presenter.js.map +1 -0
  55. package/dist/modules/ember-chat/services/emberSystemPrompt.d.ts +12 -0
  56. package/dist/modules/ember-chat/services/emberSystemPrompt.d.ts.map +1 -0
  57. package/dist/modules/ember-chat/services/emberSystemPrompt.js +29 -0
  58. package/dist/modules/ember-chat/services/emberSystemPrompt.js.map +1 -0
  59. package/dist/modules/ember-chat/usecases/askEmber/askEmber.usecase.d.ts +23 -0
  60. package/dist/modules/ember-chat/usecases/askEmber/askEmber.usecase.d.ts.map +1 -0
  61. package/dist/modules/ember-chat/usecases/askEmber/askEmber.usecase.js +23 -0
  62. package/dist/modules/ember-chat/usecases/askEmber/askEmber.usecase.js.map +1 -0
  63. package/dist/modules/ember-chat/usecases/emberSession/emberSessionRegistry.d.ts +36 -0
  64. package/dist/modules/ember-chat/usecases/emberSession/emberSessionRegistry.d.ts.map +1 -0
  65. package/dist/modules/ember-chat/usecases/emberSession/emberSessionRegistry.js +59 -0
  66. package/dist/modules/ember-chat/usecases/emberSession/emberSessionRegistry.js.map +1 -0
  67. package/dist/tests/acceptance/189-ember-readonly-review-chat.acceptance.test.d.ts +2 -0
  68. package/dist/tests/acceptance/189-ember-readonly-review-chat.acceptance.test.d.ts.map +1 -0
  69. package/dist/tests/acceptance/189-ember-readonly-review-chat.acceptance.test.js +124 -0
  70. package/dist/tests/acceptance/189-ember-readonly-review-chat.acceptance.test.js.map +1 -0
  71. package/dist/tests/factories/emberMessage.factory.d.ts +5 -0
  72. package/dist/tests/factories/emberMessage.factory.d.ts.map +1 -0
  73. package/dist/tests/factories/emberMessage.factory.js +9 -0
  74. package/dist/tests/factories/emberMessage.factory.js.map +1 -0
  75. package/dist/tests/stubs/emberReadData.stub.d.ts +20 -0
  76. package/dist/tests/stubs/emberReadData.stub.d.ts.map +1 -0
  77. package/dist/tests/stubs/emberReadData.stub.js +31 -0
  78. package/dist/tests/stubs/emberReadData.stub.js.map +1 -0
  79. package/dist/tests/stubs/emberSessionTransport.stub.d.ts +18 -0
  80. package/dist/tests/stubs/emberSessionTransport.stub.d.ts.map +1 -0
  81. package/dist/tests/stubs/emberSessionTransport.stub.js +75 -0
  82. package/dist/tests/stubs/emberSessionTransport.stub.js.map +1 -0
  83. package/dist/tests/units/dashboard/modules/emberChat.test.d.ts +2 -0
  84. package/dist/tests/units/dashboard/modules/emberChat.test.d.ts.map +1 -0
  85. package/dist/tests/units/dashboard/modules/emberChat.test.js +59 -0
  86. package/dist/tests/units/dashboard/modules/emberChat.test.js.map +1 -0
  87. package/dist/tests/units/modules/ember-chat/controllers/emberChat.routes.test.d.ts +2 -0
  88. package/dist/tests/units/modules/ember-chat/controllers/emberChat.routes.test.d.ts.map +1 -0
  89. package/dist/tests/units/modules/ember-chat/controllers/emberChat.routes.test.js +101 -0
  90. package/dist/tests/units/modules/ember-chat/controllers/emberChat.routes.test.js.map +1 -0
  91. package/dist/tests/units/modules/ember-chat/entities/emberMessage.guard.test.d.ts +2 -0
  92. package/dist/tests/units/modules/ember-chat/entities/emberMessage.guard.test.d.ts.map +1 -0
  93. package/dist/tests/units/modules/ember-chat/entities/emberMessage.guard.test.js +24 -0
  94. package/dist/tests/units/modules/ember-chat/entities/emberMessage.guard.test.js.map +1 -0
  95. package/dist/tests/units/modules/ember-chat/entities/emberSessionState.test.d.ts +2 -0
  96. package/dist/tests/units/modules/ember-chat/entities/emberSessionState.test.d.ts.map +1 -0
  97. package/dist/tests/units/modules/ember-chat/entities/emberSessionState.test.js +42 -0
  98. package/dist/tests/units/modules/ember-chat/entities/emberSessionState.test.js.map +1 -0
  99. package/dist/tests/units/modules/ember-chat/gateways/emberReadData.composite.gateway.test.d.ts +2 -0
  100. package/dist/tests/units/modules/ember-chat/gateways/emberReadData.composite.gateway.test.d.ts.map +1 -0
  101. package/dist/tests/units/modules/ember-chat/gateways/emberReadData.composite.gateway.test.js +75 -0
  102. package/dist/tests/units/modules/ember-chat/gateways/emberReadData.composite.gateway.test.js.map +1 -0
  103. package/dist/tests/units/modules/ember-chat/gateways/emberStreamJson.parser.test.d.ts +2 -0
  104. package/dist/tests/units/modules/ember-chat/gateways/emberStreamJson.parser.test.d.ts.map +1 -0
  105. package/dist/tests/units/modules/ember-chat/gateways/emberStreamJson.parser.test.js +52 -0
  106. package/dist/tests/units/modules/ember-chat/gateways/emberStreamJson.parser.test.js.map +1 -0
  107. package/dist/tests/units/modules/ember-chat/presenters/emberStatus.presenter.test.d.ts +2 -0
  108. package/dist/tests/units/modules/ember-chat/presenters/emberStatus.presenter.test.d.ts.map +1 -0
  109. package/dist/tests/units/modules/ember-chat/presenters/emberStatus.presenter.test.js +27 -0
  110. package/dist/tests/units/modules/ember-chat/presenters/emberStatus.presenter.test.js.map +1 -0
  111. package/dist/tests/units/modules/ember-chat/services/emberSystemPrompt.test.d.ts +2 -0
  112. package/dist/tests/units/modules/ember-chat/services/emberSystemPrompt.test.d.ts.map +1 -0
  113. package/dist/tests/units/modules/ember-chat/services/emberSystemPrompt.test.js +41 -0
  114. package/dist/tests/units/modules/ember-chat/services/emberSystemPrompt.test.js.map +1 -0
  115. package/dist/tests/units/modules/ember-chat/usecases/askEmber.usecase.test.d.ts +2 -0
  116. package/dist/tests/units/modules/ember-chat/usecases/askEmber.usecase.test.d.ts.map +1 -0
  117. package/dist/tests/units/modules/ember-chat/usecases/askEmber.usecase.test.js +46 -0
  118. package/dist/tests/units/modules/ember-chat/usecases/askEmber.usecase.test.js.map +1 -0
  119. package/dist/tests/units/modules/ember-chat/usecases/emberSessionRegistry.test.d.ts +2 -0
  120. package/dist/tests/units/modules/ember-chat/usecases/emberSessionRegistry.test.d.ts.map +1 -0
  121. package/dist/tests/units/modules/ember-chat/usecases/emberSessionRegistry.test.js +88 -0
  122. package/dist/tests/units/modules/ember-chat/usecases/emberSessionRegistry.test.js.map +1 -0
  123. package/package.json +1 -1
@@ -0,0 +1,23 @@
1
+ import type { EnvironmentGateway } from '../../../../modules/claude-invocation/entities/billingState/environment.gateway.js';
2
+ import type { EmberMessage } from '../../../../modules/ember-chat/entities/emberMessage/emberMessage.schema.js';
3
+ import type { EmberReadDataGateway } from '../../../../modules/ember-chat/entities/emberTool/emberTool.gateway.js';
4
+ import type { EmberSessionRegistry, EmberStreamSubscriber } from '../../../../modules/ember-chat/usecases/emberSession/emberSessionRegistry.js';
5
+ export type { EmberStreamSubscriber } from '../../../../modules/ember-chat/usecases/emberSession/emberSessionRegistry.js';
6
+ export interface AskEmberDependencies {
7
+ registry: EmberSessionRegistry;
8
+ environment: EnvironmentGateway;
9
+ readData: EmberReadDataGateway;
10
+ projectPath: string;
11
+ now: () => Date;
12
+ }
13
+ export type AskEmberResult = {
14
+ status: 'streaming';
15
+ subscribe: (subscriber: EmberStreamSubscriber) => void;
16
+ } | {
17
+ status: 'unavailable';
18
+ reason: string;
19
+ } | {
20
+ status: 'billing-regression-prevented';
21
+ };
22
+ export declare function askEmber(message: EmberMessage, dependencies: AskEmberDependencies): Promise<AskEmberResult>;
23
+ //# sourceMappingURL=askEmber.usecase.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"askEmber.usecase.d.ts","sourceRoot":"","sources":["../../../../../src/modules/ember-chat/usecases/askEmber/askEmber.usecase.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0EAA0E,CAAC;AACnH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mEAAmE,CAAC;AACtG,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,8DAA8D,CAAC;AACzG,OAAO,KAAK,EACV,oBAAoB,EACpB,qBAAqB,EACtB,MAAM,oEAAoE,CAAC;AAG5E,YAAY,EAAE,qBAAqB,EAAE,MAAM,oEAAoE,CAAC;AAEhH,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,WAAW,EAAE,kBAAkB,CAAC;IAChC,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,IAAI,CAAC;CACjB;AAED,MAAM,MAAM,cAAc,GACtB;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,SAAS,EAAE,CAAC,UAAU,EAAE,qBAAqB,KAAK,IAAI,CAAA;CAAE,GAC/E;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACzC;IAAE,MAAM,EAAE,8BAA8B,CAAA;CAAE,CAAC;AAE/C,wBAAsB,QAAQ,CAC5B,OAAO,EAAE,YAAY,EACrB,YAAY,EAAE,oBAAoB,GACjC,OAAO,CAAC,cAAc,CAAC,CAwBzB"}
@@ -0,0 +1,23 @@
1
+ import { buildEmberSystemPrompt } from '../../../../modules/ember-chat/services/emberSystemPrompt.js';
2
+ export async function askEmber(message, dependencies) {
3
+ if (dependencies.environment.hasAnthropicApiKey()) {
4
+ return { status: 'billing-regression-prevented' };
5
+ }
6
+ const { readData, projectPath } = dependencies;
7
+ const systemPrompt = buildEmberSystemPrompt({
8
+ reviewScores: await readData.reviewScores(projectPath),
9
+ insights: await readData.insights(projectPath),
10
+ jobHistory: await readData.jobHistory(projectPath),
11
+ worktrees: await readData.worktrees(),
12
+ });
13
+ const result = dependencies.registry.ask({
14
+ question: message.question,
15
+ systemPrompt,
16
+ projectPath,
17
+ });
18
+ if (result.status === 'unavailable') {
19
+ return { status: 'unavailable', reason: result.reason };
20
+ }
21
+ return { status: 'streaming', subscribe: result.subscribe };
22
+ }
23
+ //# sourceMappingURL=askEmber.usecase.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"askEmber.usecase.js","sourceRoot":"","sources":["../../../../../src/modules/ember-chat/usecases/askEmber/askEmber.usecase.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,sBAAsB,EAAE,MAAM,oDAAoD,CAAC;AAiB5F,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,OAAqB,EACrB,YAAkC;IAElC,IAAI,YAAY,CAAC,WAAW,CAAC,kBAAkB,EAAE,EAAE,CAAC;QAClD,OAAO,EAAE,MAAM,EAAE,8BAA8B,EAAE,CAAC;IACpD,CAAC;IAED,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,YAAY,CAAC;IAC/C,MAAM,YAAY,GAAG,sBAAsB,CAAC;QAC1C,YAAY,EAAE,MAAM,QAAQ,CAAC,YAAY,CAAC,WAAW,CAAC;QACtD,QAAQ,EAAE,MAAM,QAAQ,CAAC,QAAQ,CAAC,WAAW,CAAC;QAC9C,UAAU,EAAE,MAAM,QAAQ,CAAC,UAAU,CAAC,WAAW,CAAC;QAClD,SAAS,EAAE,MAAM,QAAQ,CAAC,SAAS,EAAE;KACtC,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,YAAY,CAAC,QAAQ,CAAC,GAAG,CAAC;QACvC,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,YAAY;QACZ,WAAW;KACZ,CAAC,CAAC;IAEH,IAAI,MAAM,CAAC,MAAM,KAAK,aAAa,EAAE,CAAC;QACpC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;IAC1D,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC;AAC9D,CAAC"}
@@ -0,0 +1,36 @@
1
+ import type { EmberSessionTransportGateway } from '../../../../modules/ember-chat/entities/emberSession/emberSessionTransport.gateway.js';
2
+ export type EmberStatus = 'working' | 'idle' | 'error';
3
+ export interface EmberStreamSubscriber {
4
+ onStatus: (status: EmberStatus) => void;
5
+ onChunk: (text: string) => void;
6
+ onDone: () => void;
7
+ onError: (message: string) => void;
8
+ }
9
+ export type EmberAskResult = {
10
+ status: 'streaming';
11
+ subscribe: (subscriber: EmberStreamSubscriber) => void;
12
+ } | {
13
+ status: 'unavailable';
14
+ reason: string;
15
+ };
16
+ export interface EmberAskRequest {
17
+ question: string;
18
+ systemPrompt: string;
19
+ projectPath: string;
20
+ }
21
+ export interface EmberSessionRegistryDependencies {
22
+ transport: EmberSessionTransportGateway;
23
+ now: () => Date;
24
+ idleTimeoutMs: number;
25
+ }
26
+ export declare class EmberSessionRegistry {
27
+ private readonly dependencies;
28
+ private handle;
29
+ private state;
30
+ constructor(dependencies: EmberSessionRegistryDependencies);
31
+ ask(request: EmberAskRequest): EmberAskResult;
32
+ onIdle(now: Date): void;
33
+ private ensureLive;
34
+ private release;
35
+ }
36
+ //# sourceMappingURL=emberSessionRegistry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emberSessionRegistry.d.ts","sourceRoot":"","sources":["../../../../../src/modules/ember-chat/usecases/emberSession/emberSessionRegistry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,4BAA4B,EAC7B,MAAM,6EAA6E,CAAC;AAIrF,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;AAEvD,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,CAAC;IACxC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC;AAED,MAAM,MAAM,cAAc,GACtB;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,SAAS,EAAE,CAAC,UAAU,EAAE,qBAAqB,KAAK,IAAI,CAAA;CAAE,GAC/E;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9C,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gCAAgC;IAC/C,SAAS,EAAE,4BAA4B,CAAC;IACxC,GAAG,EAAE,MAAM,IAAI,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,qBAAa,oBAAoB;IAInB,OAAO,CAAC,QAAQ,CAAC,YAAY;IAHzC,OAAO,CAAC,MAAM,CAAmC;IACjD,OAAO,CAAC,KAAK,CAAoD;gBAEpC,YAAY,EAAE,gCAAgC;IAE3E,GAAG,CAAC,OAAO,EAAE,eAAe,GAAG,cAAc;IA2B7C,MAAM,CAAC,GAAG,EAAE,IAAI,GAAG,IAAI;IAQvB,OAAO,CAAC,UAAU;IAelB,OAAO,CAAC,OAAO;CAMhB"}
@@ -0,0 +1,59 @@
1
+ import { createIdleEmberSessionState } from '../../../../modules/ember-chat/entities/emberSession/emberSessionState.js';
2
+ export class EmberSessionRegistry {
3
+ dependencies;
4
+ handle = null;
5
+ state = createIdleEmberSessionState();
6
+ constructor(dependencies) {
7
+ this.dependencies = dependencies;
8
+ }
9
+ ask(request) {
10
+ const handle = this.ensureLive(request.systemPrompt, request.projectPath);
11
+ if (handle === null) {
12
+ return { status: 'unavailable', reason: 'spawn-failed' };
13
+ }
14
+ this.state = this.state.onQuestion(this.dependencies.now());
15
+ return {
16
+ status: 'streaming',
17
+ subscribe: (subscriber) => {
18
+ subscriber.onStatus('working');
19
+ handle.onChunk((text) => subscriber.onChunk(text));
20
+ handle.onError((message) => {
21
+ subscriber.onStatus('error');
22
+ subscriber.onError(message);
23
+ });
24
+ handle.onDone(() => {
25
+ this.state = this.state.onAnswerDone(this.dependencies.now());
26
+ subscriber.onStatus('idle');
27
+ subscriber.onDone();
28
+ });
29
+ handle.ask(request.question);
30
+ },
31
+ };
32
+ }
33
+ onIdle(now) {
34
+ const next = this.state.onIdleTick(now, this.dependencies.idleTimeoutMs);
35
+ if (next.phase === 'idle' && this.state.phase === 'live') {
36
+ this.release();
37
+ }
38
+ this.state = next;
39
+ }
40
+ ensureLive(systemPrompt, projectPath) {
41
+ if (this.handle?.isAlive() === true && !this.state.needsProcess()) {
42
+ return this.handle;
43
+ }
44
+ const result = this.dependencies.transport.spawn({ systemPrompt, projectPath });
45
+ if (result.status === 'failed') {
46
+ this.handle = null;
47
+ return null;
48
+ }
49
+ this.handle = result.handle;
50
+ return this.handle;
51
+ }
52
+ release() {
53
+ if (this.handle !== null) {
54
+ this.handle.kill();
55
+ this.handle = null;
56
+ }
57
+ }
58
+ }
59
+ //# sourceMappingURL=emberSessionRegistry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emberSessionRegistry.js","sourceRoot":"","sources":["../../../../../src/modules/ember-chat/usecases/emberSession/emberSessionRegistry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=189-ember-readonly-review-chat.acceptance.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"189-ember-readonly-review-chat.acceptance.test.d.ts","sourceRoot":"","sources":["../../../src/tests/acceptance/189-ember-readonly-review-chat.acceptance.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,124 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { ProjectStatsFactory, ReviewStatsFactory } from '../../tests/factories/projectStats.factory.js';
3
+ import { StubEnvironmentGateway } from '../../tests/stubs/environment.stub.js';
4
+ import { StubEmberReadDataGateway } from '../../tests/stubs/emberReadData.stub.js';
5
+ import { StubEmberSessionTransportGateway } from '../../tests/stubs/emberSessionTransport.stub.js';
6
+ import { EmberSessionRegistry } from '../../modules/ember-chat/usecases/emberSession/emberSessionRegistry.js';
7
+ import { askEmber } from '../../modules/ember-chat/usecases/askEmber/askEmber.usecase.js';
8
+ const PROJECT_PATH = '/projects/alpha';
9
+ const FIXED_NOW = () => new Date('2026-05-28T10:00:00Z');
10
+ function fixedReviewData() {
11
+ const worstProject = ProjectStatsFactory.withReviews([
12
+ ReviewStatsFactory.create({ mrNumber: 42, score: 3, blocking: 4, warnings: 1 }),
13
+ ReviewStatsFactory.create({ mrNumber: 43, score: 4, blocking: 2, warnings: 0 }),
14
+ ]);
15
+ const readData = new StubEmberReadDataGateway();
16
+ readData.setReviewScores(PROJECT_PATH, worstProject);
17
+ return readData;
18
+ }
19
+ function buildRegistry(transport) {
20
+ return new EmberSessionRegistry({ transport, now: FIXED_NOW, idleTimeoutMs: 60_000 });
21
+ }
22
+ function buildEnvironment(hasApiKey) {
23
+ const environment = new StubEnvironmentGateway();
24
+ environment.setHasAnthropicApiKey(hasApiKey);
25
+ return environment;
26
+ }
27
+ function collectStream(subscribe) {
28
+ return new Promise((resolve) => {
29
+ let answer = '';
30
+ const statuses = [];
31
+ subscribe({
32
+ onStatus: (state) => {
33
+ statuses.push(state);
34
+ },
35
+ onChunk: (text) => {
36
+ answer += text;
37
+ },
38
+ onDone: () => {
39
+ resolve({ answer, statuses });
40
+ },
41
+ onError: () => {
42
+ resolve({ answer, statuses });
43
+ },
44
+ });
45
+ });
46
+ }
47
+ describe('Ask Ember about your reviews (acceptance, SPEC-189 Phase A)', () => {
48
+ describe('Ember answers from review data with a working then idle status sequence', () => {
49
+ it('ask about scores: the answer is grounded in the current review scores', async () => {
50
+ const readData = fixedReviewData();
51
+ const transport = new StubEmberSessionTransportGateway();
52
+ transport.answerFromSystemPrompt();
53
+ const result = await askEmber({ question: 'Quel projet a le pire score moyen cette semaine ?' }, {
54
+ registry: buildRegistry(transport),
55
+ environment: buildEnvironment(false),
56
+ readData,
57
+ projectPath: PROJECT_PATH,
58
+ now: FIXED_NOW,
59
+ });
60
+ expect(result.status).toBe('streaming');
61
+ if (result.status !== 'streaming') {
62
+ return;
63
+ }
64
+ const { answer, statuses } = await collectStream(result.subscribe);
65
+ // "42" reaches the answer only by traversing the production grounding path:
66
+ // readData -> askEmber -> system prompt -> session. The stub echoes the prompt.
67
+ expect(answer).toContain('42');
68
+ expect(statuses[0]).toBe('working');
69
+ expect(statuses.at(-1)).toBe('idle');
70
+ });
71
+ });
72
+ describe('Ember requires no API key', () => {
73
+ it('billing regression: declines to spawn when an Anthropic API key is present', async () => {
74
+ const transport = new StubEmberSessionTransportGateway();
75
+ transport.answerFromSystemPrompt();
76
+ const result = await askEmber({ question: 'Quel projet a le pire score moyen cette semaine ?' }, {
77
+ registry: buildRegistry(transport),
78
+ environment: buildEnvironment(true),
79
+ readData: fixedReviewData(),
80
+ projectPath: PROJECT_PATH,
81
+ now: FIXED_NOW,
82
+ });
83
+ expect(result.status).toBe('billing-regression-prevented');
84
+ expect(transport.spawnCount).toBe(0);
85
+ });
86
+ });
87
+ describe('within a session, consecutive questions reuse one Ember', () => {
88
+ it('follow-up keeps context: a second question does not cold-start a new session', async () => {
89
+ const transport = new StubEmberSessionTransportGateway();
90
+ transport.answerFromSystemPrompt();
91
+ const deps = {
92
+ registry: buildRegistry(transport),
93
+ environment: buildEnvironment(false),
94
+ readData: fixedReviewData(),
95
+ projectPath: PROJECT_PATH,
96
+ now: FIXED_NOW,
97
+ };
98
+ const first = await askEmber({ question: 'Quel projet a le pire score moyen ?' }, deps);
99
+ if (first.status === 'streaming') {
100
+ await collectStream(first.subscribe);
101
+ }
102
+ const second = await askEmber({ question: 'Et le mois dernier ?' }, deps);
103
+ if (second.status === 'streaming') {
104
+ await collectStream(second.subscribe);
105
+ }
106
+ expect(transport.spawnCount).toBe(1);
107
+ });
108
+ });
109
+ describe('when Ember cannot be reached, the chat reports unavailability', () => {
110
+ it('assistant unreachable: returns unavailable when the transport fails to spawn', async () => {
111
+ const transport = new StubEmberSessionTransportGateway();
112
+ transport.failSpawn();
113
+ const result = await askEmber({ question: 'Quel projet a le pire score moyen ?' }, {
114
+ registry: buildRegistry(transport),
115
+ environment: buildEnvironment(false),
116
+ readData: fixedReviewData(),
117
+ projectPath: PROJECT_PATH,
118
+ now: FIXED_NOW,
119
+ });
120
+ expect(result.status).toBe('unavailable');
121
+ });
122
+ });
123
+ });
124
+ //# sourceMappingURL=189-ember-readonly-review-chat.acceptance.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"189-ember-readonly-review-chat.acceptance.test.js","sourceRoot":"","sources":["../../../src/tests/acceptance/189-ember-readonly-review-chat.acceptance.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,2CAA2C,CAAC;AACpG,OAAO,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AAC3E,OAAO,EAAE,wBAAwB,EAAE,MAAM,qCAAqC,CAAC;AAC/E,OAAO,EAAE,gCAAgC,EAAE,MAAM,6CAA6C,CAAC;AAC/F,OAAO,EAAE,oBAAoB,EAAE,MAAM,oEAAoE,CAAC;AAC1G,OAAO,EAAE,QAAQ,EAAE,MAAM,4DAA4D,CAAC;AAGtF,MAAM,YAAY,GAAG,iBAAiB,CAAC;AACvC,MAAM,SAAS,GAAG,GAAS,EAAE,CAAC,IAAI,IAAI,CAAC,sBAAsB,CAAC,CAAC;AAE/D,SAAS,eAAe;IACtB,MAAM,YAAY,GAAG,mBAAmB,CAAC,WAAW,CAAC;QACnD,kBAAkB,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;QAC/E,kBAAkB,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;KAChF,CAAC,CAAC;IACH,MAAM,QAAQ,GAAG,IAAI,wBAAwB,EAAE,CAAC;IAChD,QAAQ,CAAC,eAAe,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;IACrD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,aAAa,CAAC,SAA2C;IAChE,OAAO,IAAI,oBAAoB,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,EAAE,CAAC,CAAC;AACxF,CAAC;AAED,SAAS,gBAAgB,CAAC,SAAkB;IAC1C,MAAM,WAAW,GAAG,IAAI,sBAAsB,EAAE,CAAC;IACjD,WAAW,CAAC,qBAAqB,CAAC,SAAS,CAAC,CAAC;IAC7C,OAAO,WAAW,CAAC;AACrB,CAAC;AAED,SAAS,aAAa,CAAC,SAAsD;IAI3E,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,MAAM,QAAQ,GAAa,EAAE,CAAC;QAC9B,SAAS,CAAC;YACR,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE;gBAClB,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACvB,CAAC;YACD,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;gBAChB,MAAM,IAAI,IAAI,CAAC;YACjB,CAAC;YACD,MAAM,EAAE,GAAG,EAAE;gBACX,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;YAChC,CAAC;YACD,OAAO,EAAE,GAAG,EAAE;gBACZ,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;YAChC,CAAC;SACF,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,QAAQ,CAAC,6DAA6D,EAAE,GAAG,EAAE;IAC3E,QAAQ,CAAC,yEAAyE,EAAE,GAAG,EAAE;QACvF,EAAE,CAAC,uEAAuE,EAAE,KAAK,IAAI,EAAE;YACrF,MAAM,QAAQ,GAAG,eAAe,EAAE,CAAC;YACnC,MAAM,SAAS,GAAG,IAAI,gCAAgC,EAAE,CAAC;YACzD,SAAS,CAAC,sBAAsB,EAAE,CAAC;YAEnC,MAAM,MAAM,GAAG,MAAM,QAAQ,CAC3B,EAAE,QAAQ,EAAE,mDAAmD,EAAE,EACjE;gBACE,QAAQ,EAAE,aAAa,CAAC,SAAS,CAAC;gBAClC,WAAW,EAAE,gBAAgB,CAAC,KAAK,CAAC;gBACpC,QAAQ;gBACR,WAAW,EAAE,YAAY;gBACzB,GAAG,EAAE,SAAS;aACf,CACF,CAAC;YAEF,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YACxC,IAAI,MAAM,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;gBAClC,OAAO;YACT,CAAC;YAED,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YAEnE,4EAA4E;YAC5E,gFAAgF;YAChF,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YAC/B,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACpC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,2BAA2B,EAAE,GAAG,EAAE;QACzC,EAAE,CAAC,4EAA4E,EAAE,KAAK,IAAI,EAAE;YAC1F,MAAM,SAAS,GAAG,IAAI,gCAAgC,EAAE,CAAC;YACzD,SAAS,CAAC,sBAAsB,EAAE,CAAC;YAEnC,MAAM,MAAM,GAAG,MAAM,QAAQ,CAC3B,EAAE,QAAQ,EAAE,mDAAmD,EAAE,EACjE;gBACE,QAAQ,EAAE,aAAa,CAAC,SAAS,CAAC;gBAClC,WAAW,EAAE,gBAAgB,CAAC,IAAI,CAAC;gBACnC,QAAQ,EAAE,eAAe,EAAE;gBAC3B,WAAW,EAAE,YAAY;gBACzB,GAAG,EAAE,SAAS;aACf,CACF,CAAC;YAEF,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC;YAC3D,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,yDAAyD,EAAE,GAAG,EAAE;QACvE,EAAE,CAAC,8EAA8E,EAAE,KAAK,IAAI,EAAE;YAC5F,MAAM,SAAS,GAAG,IAAI,gCAAgC,EAAE,CAAC;YACzD,SAAS,CAAC,sBAAsB,EAAE,CAAC;YACnC,MAAM,IAAI,GAAG;gBACX,QAAQ,EAAE,aAAa,CAAC,SAAS,CAAC;gBAClC,WAAW,EAAE,gBAAgB,CAAC,KAAK,CAAC;gBACpC,QAAQ,EAAE,eAAe,EAAE;gBAC3B,WAAW,EAAE,YAAY;gBACzB,GAAG,EAAE,SAAS;aACf,CAAC;YAEF,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,EAAE,QAAQ,EAAE,qCAAqC,EAAE,EAAE,IAAI,CAAC,CAAC;YACxF,IAAI,KAAK,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;gBACjC,MAAM,aAAa,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;YACvC,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,EAAE,QAAQ,EAAE,sBAAsB,EAAE,EAAE,IAAI,CAAC,CAAC;YAC1E,IAAI,MAAM,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;gBAClC,MAAM,aAAa,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YACxC,CAAC;YAED,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,+DAA+D,EAAE,GAAG,EAAE;QAC7E,EAAE,CAAC,8EAA8E,EAAE,KAAK,IAAI,EAAE;YAC5F,MAAM,SAAS,GAAG,IAAI,gCAAgC,EAAE,CAAC;YACzD,SAAS,CAAC,SAAS,EAAE,CAAC;YAEtB,MAAM,MAAM,GAAG,MAAM,QAAQ,CAC3B,EAAE,QAAQ,EAAE,qCAAqC,EAAE,EACnD;gBACE,QAAQ,EAAE,aAAa,CAAC,SAAS,CAAC;gBAClC,WAAW,EAAE,gBAAgB,CAAC,KAAK,CAAC;gBACpC,QAAQ,EAAE,eAAe,EAAE;gBAC3B,WAAW,EAAE,YAAY;gBACzB,GAAG,EAAE,SAAS;aACf,CACF,CAAC;YAEF,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAC5C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { EmberMessage } from '../../modules/ember-chat/entities/emberMessage/emberMessage.schema.js';
2
+ export declare class EmberMessageFactory {
3
+ static create(overrides?: Partial<EmberMessage>): EmberMessage;
4
+ }
5
+ //# sourceMappingURL=emberMessage.factory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emberMessage.factory.d.ts","sourceRoot":"","sources":["../../../src/tests/factories/emberMessage.factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mEAAmE,CAAC;AAEtG,qBAAa,mBAAmB;IAC9B,MAAM,CAAC,MAAM,CAAC,SAAS,GAAE,OAAO,CAAC,YAAY,CAAM,GAAG,YAAY;CAMnE"}
@@ -0,0 +1,9 @@
1
+ export class EmberMessageFactory {
2
+ static create(overrides = {}) {
3
+ return {
4
+ question: 'Quel projet a le pire score moyen cette semaine ?',
5
+ ...overrides,
6
+ };
7
+ }
8
+ }
9
+ //# sourceMappingURL=emberMessage.factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emberMessage.factory.js","sourceRoot":"","sources":["../../../src/tests/factories/emberMessage.factory.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,mBAAmB;IAC9B,MAAM,CAAC,MAAM,CAAC,YAAmC,EAAE;QACjD,OAAO;YACL,QAAQ,EAAE,mDAAmD;YAC7D,GAAG,SAAS;SACb,CAAC;IACJ,CAAC;CACF"}
@@ -0,0 +1,20 @@
1
+ import type { EmberReadDataGateway } from '../../modules/ember-chat/entities/emberTool/emberTool.gateway.js';
2
+ import type { ProjectStats } from '../../modules/statistics-insights/entities/stats/projectStats.js';
3
+ import type { PersistedInsightsData } from '../../modules/statistics-insights/entities/insight/persistedInsightsData.js';
4
+ import type { MrTrackingData } from '../../modules/tracking/entities/tracking/mrTrackingData.js';
5
+ import type { WorktreeEntry } from '../../modules/worktree-management/entities/worktree/worktree.schema.js';
6
+ export declare class StubEmberReadDataGateway implements EmberReadDataGateway {
7
+ private reviewScoresByProject;
8
+ private insightsByProject;
9
+ private jobHistoryByProject;
10
+ private worktreeEntries;
11
+ setReviewScores(projectPath: string, stats: ProjectStats): void;
12
+ setInsights(projectPath: string, data: PersistedInsightsData): void;
13
+ setJobHistory(projectPath: string, data: MrTrackingData): void;
14
+ setWorktrees(entries: WorktreeEntry[]): void;
15
+ reviewScores(projectPath: string): Promise<ProjectStats | null>;
16
+ insights(projectPath: string): Promise<PersistedInsightsData | null>;
17
+ jobHistory(projectPath: string): Promise<MrTrackingData | null>;
18
+ worktrees(): Promise<WorktreeEntry[]>;
19
+ }
20
+ //# sourceMappingURL=emberReadData.stub.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emberReadData.stub.d.ts","sourceRoot":"","sources":["../../../src/tests/stubs/emberReadData.stub.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,8DAA8D,CAAC;AACzG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8DAA8D,CAAC;AACjG,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,yEAAyE,CAAC;AACrH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wDAAwD,CAAC;AAC7F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oEAAoE,CAAC;AAExG,qBAAa,wBAAyB,YAAW,oBAAoB;IACnE,OAAO,CAAC,qBAAqB,CAAmC;IAChE,OAAO,CAAC,iBAAiB,CAA4C;IACrE,OAAO,CAAC,mBAAmB,CAAqC;IAChE,OAAO,CAAC,eAAe,CAAuB;IAE9C,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,IAAI;IAI/D,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,qBAAqB,GAAG,IAAI;IAInE,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,GAAG,IAAI;IAI9D,YAAY,CAAC,OAAO,EAAE,aAAa,EAAE,GAAG,IAAI;IAItC,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAI/D,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC;IAIpE,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAI/D,SAAS,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;CAG5C"}
@@ -0,0 +1,31 @@
1
+ export class StubEmberReadDataGateway {
2
+ reviewScoresByProject = new Map();
3
+ insightsByProject = new Map();
4
+ jobHistoryByProject = new Map();
5
+ worktreeEntries = [];
6
+ setReviewScores(projectPath, stats) {
7
+ this.reviewScoresByProject.set(projectPath, stats);
8
+ }
9
+ setInsights(projectPath, data) {
10
+ this.insightsByProject.set(projectPath, data);
11
+ }
12
+ setJobHistory(projectPath, data) {
13
+ this.jobHistoryByProject.set(projectPath, data);
14
+ }
15
+ setWorktrees(entries) {
16
+ this.worktreeEntries = entries;
17
+ }
18
+ async reviewScores(projectPath) {
19
+ return this.reviewScoresByProject.get(projectPath) ?? null;
20
+ }
21
+ async insights(projectPath) {
22
+ return this.insightsByProject.get(projectPath) ?? null;
23
+ }
24
+ async jobHistory(projectPath) {
25
+ return this.jobHistoryByProject.get(projectPath) ?? null;
26
+ }
27
+ async worktrees() {
28
+ return this.worktreeEntries;
29
+ }
30
+ }
31
+ //# sourceMappingURL=emberReadData.stub.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emberReadData.stub.js","sourceRoot":"","sources":["../../../src/tests/stubs/emberReadData.stub.ts"],"names":[],"mappings":"AAMA,MAAM,OAAO,wBAAwB;IAC3B,qBAAqB,GAAG,IAAI,GAAG,EAAwB,CAAC;IACxD,iBAAiB,GAAG,IAAI,GAAG,EAAiC,CAAC;IAC7D,mBAAmB,GAAG,IAAI,GAAG,EAA0B,CAAC;IACxD,eAAe,GAAoB,EAAE,CAAC;IAE9C,eAAe,CAAC,WAAmB,EAAE,KAAmB;QACtD,IAAI,CAAC,qBAAqB,CAAC,GAAG,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;IACrD,CAAC;IAED,WAAW,CAAC,WAAmB,EAAE,IAA2B;QAC1D,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;IAChD,CAAC;IAED,aAAa,CAAC,WAAmB,EAAE,IAAoB;QACrD,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;IAClD,CAAC;IAED,YAAY,CAAC,OAAwB;QACnC,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC;IACjC,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,WAAmB;QACpC,OAAO,IAAI,CAAC,qBAAqB,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,IAAI,CAAC;IAC7D,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,WAAmB;QAChC,OAAO,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,IAAI,CAAC;IACzD,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,WAAmB;QAClC,OAAO,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,IAAI,CAAC;IAC3D,CAAC;IAED,KAAK,CAAC,SAAS;QACb,OAAO,IAAI,CAAC,eAAe,CAAC;IAC9B,CAAC;CACF"}
@@ -0,0 +1,18 @@
1
+ import type { EmberSessionSpawnOptions, EmberSessionSpawnResult, EmberSessionTransportGateway } from '../../modules/ember-chat/entities/emberSession/emberSessionTransport.gateway.js';
2
+ type AnswerBuilder = (question: string, systemPrompt: string) => Promise<string>;
3
+ export declare class StubEmberSessionTransportGateway implements EmberSessionTransportGateway {
4
+ spawnCount: number;
5
+ private shouldFail;
6
+ private answerBuilder;
7
+ failSpawn(): void;
8
+ respondWith(builder: AnswerBuilder): void;
9
+ /**
10
+ * Makes the stub answer with the system prompt it was spawned with. The grounding
11
+ * data lives in that prompt, so this proves the real path: readData → askEmber →
12
+ * prompt → session, rather than the stub fabricating an answer of its own.
13
+ */
14
+ answerFromSystemPrompt(): void;
15
+ spawn(options: EmberSessionSpawnOptions): EmberSessionSpawnResult;
16
+ }
17
+ export {};
18
+ //# sourceMappingURL=emberSessionTransport.stub.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emberSessionTransport.stub.d.ts","sourceRoot":"","sources":["../../../src/tests/stubs/emberSessionTransport.stub.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAKV,wBAAwB,EACxB,uBAAuB,EACvB,4BAA4B,EAC7B,MAAM,6EAA6E,CAAC;AAErF,KAAK,aAAa,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AAqDjF,qBAAa,gCAAiC,YAAW,4BAA4B;IACnF,UAAU,SAAK;IACf,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,aAAa,CAAgE;IAErF,SAAS,IAAI,IAAI;IAIjB,WAAW,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI;IAIzC;;;;OAIG;IACH,sBAAsB,IAAI,IAAI;IAI9B,KAAK,CAAC,OAAO,EAAE,wBAAwB,GAAG,uBAAuB;CAUlE"}
@@ -0,0 +1,75 @@
1
+ class StubEmberSessionHandle {
2
+ answerBuilder;
3
+ systemPrompt;
4
+ chunkHandler = null;
5
+ doneHandler = null;
6
+ errorHandler = null;
7
+ alive = true;
8
+ constructor(answerBuilder, systemPrompt) {
9
+ this.answerBuilder = answerBuilder;
10
+ this.systemPrompt = systemPrompt;
11
+ }
12
+ ask(question) {
13
+ void this.deliver(question);
14
+ }
15
+ onChunk(handler) {
16
+ this.chunkHandler = handler;
17
+ }
18
+ onDone(handler) {
19
+ this.doneHandler = handler;
20
+ }
21
+ onError(handler) {
22
+ this.errorHandler = handler;
23
+ }
24
+ isAlive() {
25
+ return this.alive;
26
+ }
27
+ kill() {
28
+ this.alive = false;
29
+ }
30
+ async deliver(question) {
31
+ try {
32
+ const answer = await this.answerBuilder(question, this.systemPrompt);
33
+ const words = answer.split(' ');
34
+ for (let index = 0; index < words.length; index += 1) {
35
+ const fragment = index === 0 ? words[index] : ` ${words[index]}`;
36
+ this.chunkHandler?.(fragment);
37
+ }
38
+ this.doneHandler?.();
39
+ }
40
+ catch (error) {
41
+ const message = error instanceof Error ? error.message : 'stub-answer-failed';
42
+ this.errorHandler?.(message);
43
+ }
44
+ }
45
+ }
46
+ export class StubEmberSessionTransportGateway {
47
+ spawnCount = 0;
48
+ shouldFail = false;
49
+ answerBuilder = async (question) => `Réponse à : ${question}`;
50
+ failSpawn() {
51
+ this.shouldFail = true;
52
+ }
53
+ respondWith(builder) {
54
+ this.answerBuilder = builder;
55
+ }
56
+ /**
57
+ * Makes the stub answer with the system prompt it was spawned with. The grounding
58
+ * data lives in that prompt, so this proves the real path: readData → askEmber →
59
+ * prompt → session, rather than the stub fabricating an answer of its own.
60
+ */
61
+ answerFromSystemPrompt() {
62
+ this.answerBuilder = async (_question, systemPrompt) => systemPrompt;
63
+ }
64
+ spawn(options) {
65
+ if (this.shouldFail) {
66
+ return { status: 'failed', reason: 'stub-spawn-failed' };
67
+ }
68
+ this.spawnCount += 1;
69
+ return {
70
+ status: 'spawned',
71
+ handle: new StubEmberSessionHandle(this.answerBuilder, options.systemPrompt),
72
+ };
73
+ }
74
+ }
75
+ //# sourceMappingURL=emberSessionTransport.stub.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emberSessionTransport.stub.js","sourceRoot":"","sources":["../../../src/tests/stubs/emberSessionTransport.stub.ts"],"names":[],"mappings":"AAYA,MAAM,sBAAsB;IAOP;IACA;IAPX,YAAY,GAA6B,IAAI,CAAC;IAC9C,WAAW,GAA4B,IAAI,CAAC;IAC5C,YAAY,GAA6B,IAAI,CAAC;IAC9C,KAAK,GAAG,IAAI,CAAC;IAErB,YACmB,aAA4B,EAC5B,YAAoB;QADpB,kBAAa,GAAb,aAAa,CAAe;QAC5B,iBAAY,GAAZ,YAAY,CAAQ;IACpC,CAAC;IAEJ,GAAG,CAAC,QAAgB;QAClB,KAAK,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC9B,CAAC;IAED,OAAO,CAAC,OAA0B;QAChC,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC;IAC9B,CAAC;IAED,MAAM,CAAC,OAAyB;QAC9B,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC;IAC7B,CAAC;IAED,OAAO,CAAC,OAA0B;QAChC,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC;IAC9B,CAAC;IAED,OAAO;QACL,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED,IAAI;QACF,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;IAEO,KAAK,CAAC,OAAO,CAAC,QAAgB;QACpC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;YACrE,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAChC,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;gBACrD,MAAM,QAAQ,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;gBACjE,IAAI,CAAC,YAAY,EAAE,CAAC,QAAQ,CAAC,CAAC;YAChC,CAAC;YACD,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;QACvB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,oBAAoB,CAAC;YAC9E,IAAI,CAAC,YAAY,EAAE,CAAC,OAAO,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC;CACF;AAED,MAAM,OAAO,gCAAgC;IAC3C,UAAU,GAAG,CAAC,CAAC;IACP,UAAU,GAAG,KAAK,CAAC;IACnB,aAAa,GAAkB,KAAK,EAAE,QAAQ,EAAE,EAAE,CAAC,eAAe,QAAQ,EAAE,CAAC;IAErF,SAAS;QACP,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;IACzB,CAAC;IAED,WAAW,CAAC,OAAsB;QAChC,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC;IAC/B,CAAC;IAED;;;;OAIG;IACH,sBAAsB;QACpB,IAAI,CAAC,aAAa,GAAG,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,EAAE,CAAC,YAAY,CAAC;IACvE,CAAC;IAED,KAAK,CAAC,OAAiC;QACrC,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,mBAAmB,EAAE,CAAC;QAC3D,CAAC;QACD,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC;QACrB,OAAO;YACL,MAAM,EAAE,SAAS;YACjB,MAAM,EAAE,IAAI,sBAAsB,CAAC,IAAI,CAAC,aAAa,EAAE,OAAO,CAAC,YAAY,CAAC;SAC7E,CAAC;IACJ,CAAC;CACF"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=emberChat.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emberChat.test.d.ts","sourceRoot":"","sources":["../../../../../src/tests/units/dashboard/modules/emberChat.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,59 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { parseEmberEvent, foldAnswer, avatarStateFromEvent, shouldShowRetry, shouldSendQuestion, } from '../../../../dashboard/modules/emberChat.js';
3
+ describe('parseEmberEvent', () => {
4
+ it('parses a chunk event', () => {
5
+ const event = parseEmberEvent('{"type":"chunk","text":"bonjour"}');
6
+ expect(event).not.toBeNull();
7
+ expect(event?.type).toBe('chunk');
8
+ });
9
+ it('returns null for a malformed payload', () => {
10
+ expect(parseEmberEvent('not json')).toBeNull();
11
+ });
12
+ it('returns null for an event without a type', () => {
13
+ expect(parseEmberEvent('{"text":"x"}')).toBeNull();
14
+ });
15
+ });
16
+ describe('foldAnswer', () => {
17
+ it('appends chunk text progressively to the accumulated answer', () => {
18
+ const first = foldAnswer('', { type: 'chunk', text: 'Le pire ' });
19
+ const second = foldAnswer(first, { type: 'chunk', text: 'score est MR 42.' });
20
+ expect(second).toBe('Le pire score est MR 42.');
21
+ });
22
+ it('leaves the answer unchanged for a non-chunk event', () => {
23
+ expect(foldAnswer('déjà là', { type: 'status', state: 'working' })).toBe('déjà là');
24
+ });
25
+ });
26
+ describe('avatarStateFromEvent', () => {
27
+ it('maps a working status to the working avatar state', () => {
28
+ expect(avatarStateFromEvent({ type: 'status', state: 'working' })).toBe('working');
29
+ });
30
+ it('maps an idle status to the idle avatar state', () => {
31
+ expect(avatarStateFromEvent({ type: 'status', state: 'idle' })).toBe('idle');
32
+ });
33
+ it('maps an error event to the error avatar state', () => {
34
+ expect(avatarStateFromEvent({ type: 'error', message: '// EMBER INDISPONIBLE — réessayer' })).toBe('error');
35
+ });
36
+ it('returns null for an event that does not change the avatar', () => {
37
+ expect(avatarStateFromEvent({ type: 'chunk', text: 'x' })).toBeNull();
38
+ });
39
+ });
40
+ describe('shouldShowRetry', () => {
41
+ it('shows the retry control after an error event', () => {
42
+ expect(shouldShowRetry([{ type: 'error', message: 'x' }])).toBe(true);
43
+ });
44
+ it('hides the retry control while answering normally', () => {
45
+ expect(shouldShowRetry([{ type: 'status', state: 'working' }, { type: 'chunk', text: 'x' }])).toBe(false);
46
+ });
47
+ });
48
+ describe('shouldSendQuestion', () => {
49
+ it('does not send an empty question', () => {
50
+ expect(shouldSendQuestion('')).toBe(false);
51
+ });
52
+ it('does not send a whitespace-only question', () => {
53
+ expect(shouldSendQuestion(' ')).toBe(false);
54
+ });
55
+ it('sends a non-empty question', () => {
56
+ expect(shouldSendQuestion('Quel projet a le pire score ?')).toBe(true);
57
+ });
58
+ });
59
+ //# sourceMappingURL=emberChat.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emberChat.test.js","sourceRoot":"","sources":["../../../../../src/tests/units/dashboard/modules/emberChat.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=emberChat.routes.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emberChat.routes.test.d.ts","sourceRoot":"","sources":["../../../../../../src/tests/units/modules/ember-chat/controllers/emberChat.routes.test.ts"],"names":[],"mappings":""}