sloppatrol 0.1.0 → 0.1.2

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 (73) hide show
  1. package/README.md +111 -160
  2. package/dist/census/index.js +4 -2
  3. package/dist/census/index.js.map +1 -1
  4. package/dist/census/lib/guardrails.js +38 -9
  5. package/dist/census/lib/guardrails.js.map +1 -1
  6. package/dist/census/lib/stack.d.ts +2 -1
  7. package/dist/census/lib/stack.js +8 -4
  8. package/dist/census/lib/stack.js.map +1 -1
  9. package/dist/cli/index.js +20 -4
  10. package/dist/cli/index.js.map +1 -1
  11. package/dist/cli/lib/browser.d.ts +2 -0
  12. package/dist/cli/lib/browser.js +13 -0
  13. package/dist/cli/lib/browser.js.map +1 -0
  14. package/dist/cli/lib/usage.d.ts +1 -1
  15. package/dist/cli/lib/usage.js +4 -3
  16. package/dist/cli/lib/usage.js.map +1 -1
  17. package/dist/config/lib/schema.d.ts +30 -0
  18. package/dist/config/lib/schema.js +16 -0
  19. package/dist/config/lib/schema.js.map +1 -1
  20. package/dist/dashboard/index.d.ts +4 -0
  21. package/dist/dashboard/index.js +6 -2
  22. package/dist/dashboard/index.js.map +1 -1
  23. package/dist/dashboard/lib/snapshot.d.ts +168 -30
  24. package/dist/dashboard/lib/snapshot.js +104 -37
  25. package/dist/dashboard/lib/snapshot.js.map +1 -1
  26. package/dist/dashboard/lib/summary.d.ts +230 -0
  27. package/dist/dashboard/lib/summary.js +398 -0
  28. package/dist/dashboard/lib/summary.js.map +1 -0
  29. package/dist/domain/index.d.ts +2 -0
  30. package/dist/domain/index.js +1 -0
  31. package/dist/domain/index.js.map +1 -1
  32. package/dist/domain/lib/cycle.d.ts +17 -0
  33. package/dist/domain/lib/cycle.js +2 -0
  34. package/dist/domain/lib/cycle.js.map +1 -0
  35. package/dist/{patrol → domain}/lib/pick.d.ts +2 -1
  36. package/dist/{patrol → domain}/lib/pick.js.map +1 -1
  37. package/dist/domain/lib/refactor.d.ts +2 -0
  38. package/dist/gate/index.d.ts +7 -0
  39. package/dist/gate/index.js +15 -0
  40. package/dist/gate/index.js.map +1 -1
  41. package/dist/patrol/lib/attempt.js +10 -2
  42. package/dist/patrol/lib/attempt.js.map +1 -1
  43. package/dist/patrol/lib/base.d.ts +10 -3
  44. package/dist/patrol/lib/base.js +14 -8
  45. package/dist/patrol/lib/base.js.map +1 -1
  46. package/dist/patrol/lib/cycle.d.ts +1 -1
  47. package/dist/patrol/lib/cycle.js +32 -1
  48. package/dist/patrol/lib/cycle.js.map +1 -1
  49. package/dist/patrol/lib/recover.d.ts +2 -1
  50. package/dist/patrol/lib/recover.js +16 -1
  51. package/dist/patrol/lib/recover.js.map +1 -1
  52. package/dist/patrol/lib/scorecard.js +12 -4
  53. package/dist/patrol/lib/scorecard.js.map +1 -1
  54. package/dist/patrol/lib/steps.d.ts +5 -2
  55. package/dist/patrol/lib/steps.js +9 -6
  56. package/dist/patrol/lib/steps.js.map +1 -1
  57. package/dist/playbook/index.js +3 -3
  58. package/dist/roles/lib/prompts.d.ts +1 -0
  59. package/dist/roles/lib/prompts.js +6 -0
  60. package/dist/roles/lib/prompts.js.map +1 -1
  61. package/dist/roles/lib/refactorer.d.ts +1 -0
  62. package/dist/roles/lib/refactorer.js +24 -6
  63. package/dist/roles/lib/refactorer.js.map +1 -1
  64. package/dist/roles/lib/scorecard-text.js +1 -1
  65. package/dist/roles/lib/scorecard-text.js.map +1 -1
  66. package/dist/store/index.d.ts +4 -1
  67. package/dist/store/index.js +7 -0
  68. package/dist/store/index.js.map +1 -1
  69. package/package.json +1 -1
  70. package/ui/app.js +1236 -324
  71. package/ui/index.html +35 -13
  72. package/ui/style.css +1063 -340
  73. /package/dist/{patrol → domain}/lib/pick.js +0 -0
@@ -0,0 +1,2 @@
1
+ /** Opens `url` in the default browser. It is a convenience: when it fails, the printed URL still works. */
2
+ export declare function openInBrowser(url: string): void;
@@ -0,0 +1,13 @@
1
+ import { spawn } from 'node:child_process';
2
+ /** Opens `url` in the default browser. It is a convenience: when it fails, the printed URL still works. */
3
+ export function openInBrowser(url) {
4
+ const [command, args] = process.platform === 'darwin'
5
+ ? ['open', [url]]
6
+ : process.platform === 'win32'
7
+ ? ['cmd', ['/c', 'start', '""', url]]
8
+ : ['xdg-open', [url]];
9
+ const child = spawn(command, args, { detached: true, stdio: 'ignore' });
10
+ child.on('error', () => { });
11
+ child.unref();
12
+ }
13
+ //# sourceMappingURL=browser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.js","sourceRoot":"","sources":["../../../src/cli/lib/browser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE3C,2GAA2G;AAC3G,MAAM,UAAU,aAAa,CAAC,GAAW;IACvC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,GACnB,OAAO,CAAC,QAAQ,KAAK,QAAQ;QAC3B,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC;QACjB,CAAC,CAAC,OAAO,CAAC,QAAQ,KAAK,OAAO;YAC5B,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;YACrC,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5B,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;IACxE,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAC5B,KAAK,CAAC,KAAK,EAAE,CAAC;AAChB,CAAC"}
@@ -1 +1 @@
1
- export declare const USAGE = "sloppatrol: a team of agents that makes your code cleaner, one verified refactor at a time.\n\nStart\n sloppatrol init Create .sloppatrol/ with a guess at your checks\n sloppatrol doctor Show the config, the agent each role runs on, and what is missing\n\nRun\n sloppatrol Patrol all day: a cycle every patrol.intervalMinutes, with the dashboard\n sloppatrol --once One cycle, then stop\n sloppatrol dashboard Only the dashboard (http://127.0.0.1:4322)\n\nOne step at a time\n sloppatrol check Run your checks on the patrolled commit (the oracle)\n sloppatrol survey Let the surveyor record opportunities now\n sloppatrol refactor [id] Attempt one opportunity now: the best open one, or <id>\n sloppatrol score Score the repo now: up to metrics.panel more files (--files <n>)\n sloppatrol publish Open pull requests for verified refactors (--dry-run)\n\nRead\n sloppatrol status The opportunities, the refactors and the agents\n sloppatrol census What the surveyor sees: stack, large files, mocks, churn\n sloppatrol dismiss <id> Never propose this opportunity again (--reason \"...\")\n\nFlags\n --agent <name> init: auto (default), claude, codex or opencode\n --port <n> The dashboard port (default 4322)\n --files <n> score: how many more files to score in this run\n --no-dashboard Patrol without the dashboard\n --cwd <dir> Run as if in <dir>\n -h, --help This help\n -v, --version The version";
1
+ export declare const USAGE = "sloppatrol: a team of agents that makes your code cleaner, one verified refactor at a time.\n\nStart\n sloppatrol init Create .sloppatrol/ with a guess at your checks\n sloppatrol doctor Show the config, the agent each role runs on, and what is missing\n\nRun\n sloppatrol Patrol all day: a cycle every patrol.intervalMinutes, with the dashboard\n sloppatrol --once One cycle, then stop\n sloppatrol dashboard Only the dashboard (http://127.0.0.1:4322)\n sloppatrol score Score every file, then open the scorecard, ready to share\n\nOne step at a time\n sloppatrol check Run your checks on the patrolled commit (the oracle)\n sloppatrol survey Let the surveyor record opportunities now\n sloppatrol refactor [id] Attempt one opportunity now: the best open one, or <id>\n sloppatrol publish Open pull requests for verified refactors (--dry-run)\n\nRead\n sloppatrol status The opportunities, the refactors and the agents\n sloppatrol census What the surveyor sees: stack, large files, mocks, churn\n sloppatrol dismiss <id> Never propose this opportunity again (--reason \"...\")\n\nFlags\n --agent <name> init: auto (default), claude, codex or opencode\n --port <n> The dashboard port (default 4322)\n --files <n> score: score at most <n> more files in this run\n --no-dashboard Patrol or score without the dashboard\n --no-open score: do not open the browser\n --cwd <dir> Run as if in <dir>\n -h, --help This help\n -v, --version The version";
@@ -8,12 +8,12 @@ Run
8
8
  sloppatrol Patrol all day: a cycle every patrol.intervalMinutes, with the dashboard
9
9
  sloppatrol --once One cycle, then stop
10
10
  sloppatrol dashboard Only the dashboard (http://127.0.0.1:4322)
11
+ sloppatrol score Score every file, then open the scorecard, ready to share
11
12
 
12
13
  One step at a time
13
14
  sloppatrol check Run your checks on the patrolled commit (the oracle)
14
15
  sloppatrol survey Let the surveyor record opportunities now
15
16
  sloppatrol refactor [id] Attempt one opportunity now: the best open one, or <id>
16
- sloppatrol score Score the repo now: up to metrics.panel more files (--files <n>)
17
17
  sloppatrol publish Open pull requests for verified refactors (--dry-run)
18
18
 
19
19
  Read
@@ -24,8 +24,9 @@ Read
24
24
  Flags
25
25
  --agent <name> init: auto (default), claude, codex or opencode
26
26
  --port <n> The dashboard port (default 4322)
27
- --files <n> score: how many more files to score in this run
28
- --no-dashboard Patrol without the dashboard
27
+ --files <n> score: score at most <n> more files in this run
28
+ --no-dashboard Patrol or score without the dashboard
29
+ --no-open score: do not open the browser
29
30
  --cwd <dir> Run as if in <dir>
30
31
  -h, --help This help
31
32
  -v, --version The version`;
@@ -1 +1 @@
1
- {"version":3,"file":"usage.js","sourceRoot":"","sources":["../../../src/cli/lib/usage.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,KAAK,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gCA8BW,CAAC"}
1
+ {"version":3,"file":"usage.js","sourceRoot":"","sources":["../../../src/cli/lib/usage.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,KAAK,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gCA+BW,CAAC"}
@@ -85,6 +85,14 @@ export declare const configSchema: z.ZodObject<{
85
85
  }>]>>;
86
86
  /** Runs once in each fresh checkout or worktree, before the checks: install, codegen. */
87
87
  prepare: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
88
+ /**
89
+ * Mechanical tidy-up commands run in the refactorer's worktree after its
90
+ * session and before the gate: remove the imports and helpers an edit left
91
+ * unused, format, sort. They run on every attempt, so residue from a session
92
+ * that ran out of time never reaches a check. A failing command is logged
93
+ * and left to the checks: cleanup never decides the verdict.
94
+ */
95
+ cleanup: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
88
96
  /**
89
97
  * The oracle. Each check must pass on the patrolled commit before
90
98
  * Sloppatrol starts work, and again on every refactor.
@@ -213,6 +221,14 @@ export declare const configSchema: z.ZodObject<{
213
221
  maxPerCycle: z.ZodDefault<z.ZodNumber>;
214
222
  /** Attempts per opportunity before it is exhausted. */
215
223
  maxAttempts: z.ZodDefault<z.ZodNumber>;
224
+ /**
225
+ * A session that stops at its timeout hands its worktree to one
226
+ * bounded completion session, which finishes the change and tidies
227
+ * what it left unused instead of letting the gate reject a
228
+ * half-finished edit. These are that session's limits.
229
+ */
230
+ cleanupTimeoutMinutes: z.ZodDefault<z.ZodNumber>;
231
+ cleanupMaxSteps: z.ZodDefault<z.ZodNumber>;
216
232
  /** Overrides the top-level `agent` for this role. */
217
233
  use: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<"auto">, z.ZodEffects<z.ZodEnum<["claude", "codex", "opencode"]>, {
218
234
  runtime: "cli";
@@ -255,6 +271,8 @@ export declare const configSchema: z.ZodObject<{
255
271
  maxSteps: number;
256
272
  maxPerCycle: number;
257
273
  maxAttempts: number;
274
+ cleanupTimeoutMinutes: number;
275
+ cleanupMaxSteps: number;
258
276
  use?: "auto" | {
259
277
  runtime: "cli";
260
278
  agent: "claude" | "codex" | "opencode";
@@ -286,6 +304,8 @@ export declare const configSchema: z.ZodObject<{
286
304
  budgetUsd?: number | undefined;
287
305
  maxPerCycle?: number | undefined;
288
306
  maxAttempts?: number | undefined;
307
+ cleanupTimeoutMinutes?: number | undefined;
308
+ cleanupMaxSteps?: number | undefined;
289
309
  }>>;
290
310
  /** Scores files against the metrics rubric, for the scorecard and for each refactor's proof. */
291
311
  assessor: z.ZodDefault<z.ZodObject<{
@@ -457,6 +477,8 @@ export declare const configSchema: z.ZodObject<{
457
477
  maxSteps: number;
458
478
  maxPerCycle: number;
459
479
  maxAttempts: number;
480
+ cleanupTimeoutMinutes: number;
481
+ cleanupMaxSteps: number;
460
482
  use?: "auto" | {
461
483
  runtime: "cli";
462
484
  agent: "claude" | "codex" | "opencode";
@@ -545,6 +567,8 @@ export declare const configSchema: z.ZodObject<{
545
567
  budgetUsd?: number | undefined;
546
568
  maxPerCycle?: number | undefined;
547
569
  maxAttempts?: number | undefined;
570
+ cleanupTimeoutMinutes?: number | undefined;
571
+ cleanupMaxSteps?: number | undefined;
548
572
  } | undefined;
549
573
  assessor?: {
550
574
  timeoutMinutes?: number | undefined;
@@ -653,6 +677,7 @@ export declare const configSchema: z.ZodObject<{
653
677
  source: string;
654
678
  };
655
679
  prepare: string[];
680
+ cleanup: string[];
656
681
  checks: {
657
682
  tests: boolean;
658
683
  name: string;
@@ -694,6 +719,8 @@ export declare const configSchema: z.ZodObject<{
694
719
  maxSteps: number;
695
720
  maxPerCycle: number;
696
721
  maxAttempts: number;
722
+ cleanupTimeoutMinutes: number;
723
+ cleanupMaxSteps: number;
697
724
  use?: "auto" | {
698
725
  runtime: "cli";
699
726
  agent: "claude" | "codex" | "opencode";
@@ -788,6 +815,7 @@ export declare const configSchema: z.ZodObject<{
788
815
  source?: string | undefined;
789
816
  } | undefined;
790
817
  prepare?: string[] | undefined;
818
+ cleanup?: string[] | undefined;
791
819
  uncheckedTests?: string[] | undefined;
792
820
  focus?: string[] | undefined;
793
821
  ignore?: string[] | undefined;
@@ -831,6 +859,8 @@ export declare const configSchema: z.ZodObject<{
831
859
  budgetUsd?: number | undefined;
832
860
  maxPerCycle?: number | undefined;
833
861
  maxAttempts?: number | undefined;
862
+ cleanupTimeoutMinutes?: number | undefined;
863
+ cleanupMaxSteps?: number | undefined;
834
864
  } | undefined;
835
865
  assessor?: {
836
866
  timeoutMinutes?: number | undefined;
@@ -43,6 +43,14 @@ export const configSchema = z.object({
43
43
  agent: runtimeSchema.default('auto'),
44
44
  /** Runs once in each fresh checkout or worktree, before the checks: install, codegen. */
45
45
  prepare: z.array(z.string()).default([]),
46
+ /**
47
+ * Mechanical tidy-up commands run in the refactorer's worktree after its
48
+ * session and before the gate: remove the imports and helpers an edit left
49
+ * unused, format, sort. They run on every attempt, so residue from a session
50
+ * that ran out of time never reaches a check. A failing command is logged
51
+ * and left to the checks: cleanup never decides the verdict.
52
+ */
53
+ cleanup: z.array(z.string()).default([]),
46
54
  /**
47
55
  * The oracle. Each check must pass on the patrolled commit before
48
56
  * Sloppatrol starts work, and again on every refactor.
@@ -84,6 +92,14 @@ export const configSchema = z.object({
84
92
  maxPerCycle: z.number().int().positive().default(1),
85
93
  /** Attempts per opportunity before it is exhausted. */
86
94
  maxAttempts: z.number().int().positive().default(2),
95
+ /**
96
+ * A session that stops at its timeout hands its worktree to one
97
+ * bounded completion session, which finishes the change and tidies
98
+ * what it left unused instead of letting the gate reject a
99
+ * half-finished edit. These are that session's limits.
100
+ */
101
+ cleanupTimeoutMinutes: z.number().positive().default(15),
102
+ cleanupMaxSteps: z.number().int().positive().default(30),
87
103
  })
88
104
  .default({}),
89
105
  /** Scores files against the metrics rubric, for the scorecard and for each refactor's proof. */
@@ -1 +1 @@
1
- {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/config/lib/schema.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/config/lib/schema.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import type { Store } from '../store/index.js';
2
2
  export type { DashboardState } from './lib/snapshot.js';
3
+ export type { ProjectSummary } from './lib/summary.js';
3
4
  export type Dashboard = {
4
5
  url: string;
5
6
  close(): Promise<void>;
@@ -7,9 +8,12 @@ export type Dashboard = {
7
8
  /**
8
9
  * A read-only local page over the store: the agents, the backlog, the
9
10
  * refactors and their gate, and every check run, refreshed live.
11
+ * `/api/summary` holds every number the pages show, computed once here.
10
12
  */
11
13
  export declare function startDashboard(options: {
12
14
  store: Store;
13
15
  project: string;
14
16
  port?: number;
17
+ /** Whether a process runs, by its id. The tests pass their own. */
18
+ alive?: (pid: number) => boolean;
15
19
  }): Promise<Dashboard>;
@@ -1,7 +1,7 @@
1
1
  import { readFile } from 'node:fs/promises';
2
2
  import { createServer } from 'node:http';
3
3
  import { fileURLToPath } from 'node:url';
4
- import { snapshot } from './lib/snapshot.js';
4
+ import { snapshots } from './lib/snapshot.js';
5
5
  /** Shipped in the npm package next to `dist/`. */
6
6
  const UI = fileURLToPath(new URL('../../ui/', import.meta.url));
7
7
  const STATIC = {
@@ -17,9 +17,11 @@ function json(response, status, body) {
17
17
  /**
18
18
  * A read-only local page over the store: the agents, the backlog, the
19
19
  * refactors and their gate, and every check run, refreshed live.
20
+ * `/api/summary` holds every number the pages show, computed once here.
20
21
  */
21
22
  export async function startDashboard(options) {
22
23
  const { store } = options;
24
+ const read = snapshots(store, options.project, options.alive);
23
25
  const server = createServer(async (request, response) => {
24
26
  try {
25
27
  const url = new URL(request.url ?? '/', 'http://localhost');
@@ -30,7 +32,9 @@ export async function startDashboard(options) {
30
32
  return;
31
33
  }
32
34
  if (url.pathname === '/api/state')
33
- return json(response, 200, await snapshot(store, options.project));
35
+ return json(response, 200, await read.state());
36
+ if (url.pathname === '/api/summary')
37
+ return json(response, 200, await read.summary());
34
38
  const [, api, kind, id] = url.pathname.split('/');
35
39
  if (api === 'api' && id && ID.test(id)) {
36
40
  if (kind === 'sessions')
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/dashboard/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/dashboard/index.ts"],"names":[],"mappings":"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"}
@@ -1,32 +1,170 @@
1
- import type { AgentEvent, CheckRun } from '../../domain/index.js';
2
1
  import type { Store } from '../../store/index.js';
3
- export type SessionSummary = {
4
- id: string;
5
- role: AgentEvent['role'];
6
- subject?: string;
7
- startedAt: string;
8
- endedAt?: string;
9
- state: 'running' | 'done' | 'failed';
10
- last: string;
11
- costUsd: number;
2
+ import { type LiveStatus, type ProjectSummary, type SessionRow } from './summary.js';
3
+ export type DashboardState = Awaited<ReturnType<ReturnType<typeof snapshots>['state']>>;
4
+ /**
5
+ * Reads the store for the dashboard. A session that ended never changes, so
6
+ * its row is read once; the counts still cover every session.
7
+ */
8
+ export declare function snapshots(store: Store, project: string, alive?: (pid: number) => boolean): {
9
+ summary(): Promise<ProjectSummary>;
10
+ /** Everything the dashboard shows, in one read of the store. */
11
+ state(): Promise<{
12
+ project: string;
13
+ now: string;
14
+ summary: {
15
+ checks: {
16
+ caught?: number | undefined;
17
+ planted?: number | undefined;
18
+ runs: number;
19
+ problems: number;
20
+ base: {
21
+ runs: number;
22
+ bad: number;
23
+ problems: number;
24
+ };
25
+ gate: {
26
+ runs: number;
27
+ bad: number;
28
+ problems: number;
29
+ };
30
+ refactorer: {
31
+ runs: number;
32
+ bad: number;
33
+ problems: number;
34
+ };
35
+ mutation: {
36
+ runs: number;
37
+ bad: number;
38
+ problems: number;
39
+ };
40
+ };
41
+ sessions: {
42
+ total: number;
43
+ running: number;
44
+ failed: number;
45
+ failedRecent: number;
46
+ stopped: number;
47
+ costUsd: number;
48
+ costRecentUsd: number;
49
+ byRole: {
50
+ [k: string]: number;
51
+ };
52
+ };
53
+ attention: {
54
+ tone: import("./summary.js").Tone;
55
+ title: string;
56
+ detail: string;
57
+ command?: string;
58
+ target?: {
59
+ tab: string;
60
+ filter?: string;
61
+ select?: string;
62
+ };
63
+ links?: {
64
+ label: string;
65
+ href: string;
66
+ select?: string;
67
+ }[];
68
+ }[];
69
+ quality?: {
70
+ compared: number;
71
+ trend: number[];
72
+ metrics: {
73
+ trend: number[];
74
+ delta?: number | undefined;
75
+ id: "tests" | "simplicity" | "modularity" | "injection" | "purity" | "clarity" | "guardrails";
76
+ name: string;
77
+ value: number;
78
+ }[];
79
+ coverage: {
80
+ files: number;
81
+ totalFiles: number;
82
+ share: number;
83
+ };
84
+ guardrails: {
85
+ present: number;
86
+ total: number;
87
+ };
88
+ delta?: number | undefined;
89
+ overall?: number | undefined;
90
+ sha: string;
91
+ at: string;
92
+ rubric: string;
93
+ } | undefined;
94
+ cycles: {
95
+ last?: {
96
+ at: string;
97
+ tone: import("./summary.js").Tone;
98
+ text: string;
99
+ sha?: string;
100
+ } | undefined;
101
+ count: number;
102
+ };
103
+ refactors: {
104
+ rejectedBy: {
105
+ step: string;
106
+ label: string;
107
+ count: number;
108
+ }[];
109
+ keptRate?: number | undefined;
110
+ total: number;
111
+ working: number;
112
+ waits: number;
113
+ rejected: number;
114
+ error: number;
115
+ merged: number;
116
+ closed: number;
117
+ judged: number;
118
+ kept: number;
119
+ };
120
+ decisions: {
121
+ toPublish: number;
122
+ toReview: number;
123
+ };
124
+ backlog: {
125
+ total: number;
126
+ open: number;
127
+ ready: string[];
128
+ busy: string[];
129
+ working: string[];
130
+ byStrength: Record<import("../../domain/index.js").Strength, number>;
131
+ verified: number;
132
+ proposed: number;
133
+ done: number;
134
+ exhausted: number;
135
+ dismissed: number;
136
+ };
137
+ main?: {
138
+ failed?: string | undefined;
139
+ sha: string;
140
+ state: "running" | "failed" | "passed";
141
+ runId: string;
142
+ at: string;
143
+ } | undefined;
144
+ project: string;
145
+ verdict: {
146
+ tone: import("./summary.js").Tone;
147
+ text: string;
148
+ detail: string;
149
+ };
150
+ activity: {
151
+ state: "running" | "waiting" | "stopped";
152
+ text: string;
153
+ detail: string;
154
+ };
155
+ };
156
+ patrol: import("../../store/index.js").PatrolState;
157
+ statuses: LiveStatus[];
158
+ opportunities: import("../../domain/index.js").Opportunity[];
159
+ refactors: import("./summary.js").RefactorRow[];
160
+ checkRuns: import("./summary.js").CheckRunRow[];
161
+ sessions: SessionRow[];
162
+ scorecards: import("../../domain/index.js").Scorecard[];
163
+ metrics: {
164
+ id: "tests" | "simplicity" | "modularity" | "injection" | "purity" | "clarity" | "guardrails";
165
+ name: string;
166
+ question: string;
167
+ levels: [string, string, string, string, string];
168
+ }[];
169
+ }>;
12
170
  };
13
- /** Everything the dashboard shows, in one read of the store. */
14
- export declare function snapshot(store: Store, project: string): Promise<{
15
- project: string;
16
- now: string;
17
- patrol: import("../../store/index.js").PatrolState;
18
- statuses: import("../../domain/index.js").AgentStatus[];
19
- opportunities: import("../../domain/index.js").Opportunity[];
20
- refactors: import("../../domain/index.js").Refactor[];
21
- checkRuns: CheckRun[];
22
- sessions: (SessionSummary | undefined)[];
23
- /** Oldest first, for the trend; the last one is the current scorecard. */
24
- scorecards: import("../../domain/index.js").Scorecard[];
25
- metrics: {
26
- id: "tests" | "simplicity" | "modularity" | "injection" | "purity" | "clarity" | "guardrails";
27
- name: string;
28
- question: string;
29
- levels: [string, string, string, string, string];
30
- }[];
31
- }>;
32
- export type DashboardState = Awaited<ReturnType<typeof snapshot>>;
@@ -1,9 +1,19 @@
1
1
  import { METRICS } from '../../metrics/index.js';
2
- const RECENT_SESSIONS = 40;
3
- const RECENT_CHECKS = 60;
2
+ import { checkRunRow, projectSummary, refactorRow, } from './summary.js';
3
+ const RECENT_SESSIONS = 60;
4
+ const RECENT_CHECKS = 80;
4
5
  const RECENT_SCORECARDS = 30;
5
- async function summarize(store, id) {
6
- const events = await store.events(id);
6
+ /** True when a process with this id runs. EPERM: it runs, as another user. */
7
+ function processAlive(pid) {
8
+ try {
9
+ process.kill(pid, 0);
10
+ return true;
11
+ }
12
+ catch (error) {
13
+ return error.code === 'EPERM';
14
+ }
15
+ }
16
+ function sessionRow(id, events, active) {
7
17
  const first = events[0];
8
18
  const last = events.at(-1);
9
19
  if (!first || !last)
@@ -12,10 +22,17 @@ async function summarize(store, id) {
12
22
  return {
13
23
  id,
14
24
  role: first.role,
15
- subject: first.subject,
25
+ ...(first.subject ? { subject: first.subject } : {}),
16
26
  startedAt: first.at,
17
- endedAt: ended ? last.at : undefined,
18
- state: !ended ? 'running' : last.kind === 'error' ? 'failed' : 'done',
27
+ ...(ended ? { endedAt: last.at } : {}),
28
+ // An interrupted session ended because Sloppatrol stopped, not because the agent failed.
29
+ state: !ended || /^Interrupted/.test(last.summary)
30
+ ? !ended && active
31
+ ? 'running'
32
+ : 'stopped'
33
+ : last.kind === 'error'
34
+ ? 'failed'
35
+ : 'done',
19
36
  last: last.summary.split('\n')[0].slice(0, 200),
20
37
  costUsd: events.reduce((sum, e) => sum + (e.kind === 'finish' || e.kind === 'error' ? (e.costUsd ?? 0) : 0), 0),
21
38
  };
@@ -24,37 +41,87 @@ async function summarize(store, id) {
24
41
  function light(run) {
25
42
  return { ...run, results: run.results.map((r) => ({ ...r, output: r.ok ? '' : r.output.slice(-600) })) };
26
43
  }
27
- /** Everything the dashboard shows, in one read of the store. */
28
- export async function snapshot(store, project) {
29
- const [patrol, statuses, opportunities, refactors, checkRuns, sessionIds, scorecards] = await Promise.all([
30
- store.patrolState(),
31
- store.statuses(),
32
- store.listOpportunities(),
33
- store.listRefactors(),
34
- store.listCheckRuns(),
35
- store.sessions(),
36
- store.listScorecards(),
37
- ]);
38
- const sessions = await Promise.all(sessionIds
39
- .slice(-RECENT_SESSIONS)
40
- .reverse()
41
- .map((id) => summarize(store, id)));
44
+ /**
45
+ * Reads the store for the dashboard. A session that ended never changes, so
46
+ * its row is read once; the counts still cover every session.
47
+ */
48
+ export function snapshots(store, project, alive = processAlive) {
49
+ const ended = new Map();
50
+ const sessionRows = async (ids, active) => (await Promise.all(ids.map(async (id) => {
51
+ const known = ended.get(id);
52
+ if (known)
53
+ return known;
54
+ const row = sessionRow(id, await store.events(id), active);
55
+ if (row?.endedAt)
56
+ ended.set(id, row);
57
+ return row;
58
+ }))).filter((row) => !!row);
59
+ const read = async () => {
60
+ const [patrol, rawStatuses, opportunities, refactors, checkRuns, sessionIds, scorecards, cycles] = await Promise.all([
61
+ store.patrolState(),
62
+ store.statuses(),
63
+ store.listOpportunities(),
64
+ store.listRefactors(),
65
+ store.listCheckRuns(),
66
+ store.sessions(),
67
+ store.listScorecards(),
68
+ store.listCycles(),
69
+ ]);
70
+ const statuses = rawStatuses.map((s) => {
71
+ const running = alive(s.pid);
72
+ return { ...s, alive: running, stale: s.state === 'running' && !running };
73
+ });
74
+ // Work marked as running is real only while a Sloppatrol process runs.
75
+ const active = statuses.some((s) => s.alive);
76
+ const byId = new Map(opportunities.map((o) => [o.id, o]));
77
+ const refactorRows = refactors.map((r) => refactorRow(r, byId.get(r.opportunityId), checkRuns, active));
78
+ const runRows = checkRuns.map((run) => checkRunRow(run, active));
79
+ const sessions = await sessionRows(sessionIds, active);
80
+ const now = new Date();
81
+ const summary = projectSummary({
82
+ project,
83
+ now,
84
+ patrol,
85
+ statuses,
86
+ opportunities,
87
+ refactors: refactorRows,
88
+ checkRuns: runRows,
89
+ scorecards,
90
+ sessions,
91
+ cycles,
92
+ });
93
+ return { now, patrol, statuses, opportunities, refactorRows, runRows, sessions, scorecards, summary };
94
+ };
42
95
  return {
43
- project,
44
- now: new Date().toISOString(),
45
- patrol,
46
- statuses,
47
- opportunities: opportunities.reverse(),
48
- refactors: refactors.reverse(),
49
- checkRuns: checkRuns.slice(-RECENT_CHECKS).reverse().map(light),
50
- sessions: sessions.filter(Boolean),
51
- /** Oldest first, for the trend; the last one is the current scorecard. */
52
- // Every file's scores for the newest scorecard only: older ones feed the
53
- // trend, which needs their means alone.
54
- scorecards: scorecards
55
- .slice(-RECENT_SCORECARDS)
56
- .map((card, i, all) => (i === all.length - 1 ? card : { ...card, units: [] })),
57
- metrics: METRICS.map((m) => ({ id: m.id, name: m.name, question: m.question, levels: m.levels })),
96
+ async summary() {
97
+ return (await read()).summary;
98
+ },
99
+ /** Everything the dashboard shows, in one read of the store. */
100
+ async state() {
101
+ const data = await read();
102
+ return {
103
+ project,
104
+ now: data.now.toISOString(),
105
+ summary: data.summary,
106
+ patrol: data.patrol,
107
+ statuses: data.statuses,
108
+ opportunities: data.opportunities.reverse(),
109
+ // The gate's check outputs are one request away, in their check run.
110
+ refactors: data.refactorRows
111
+ .reverse()
112
+ .map((r) => r.verdict
113
+ ? { ...r, verdict: { ...r.verdict, checks: r.verdict.checks.map((c) => ({ ...c, output: '' })) } }
114
+ : r),
115
+ checkRuns: data.runRows.slice(-RECENT_CHECKS).reverse().map(light),
116
+ sessions: data.sessions.slice(-RECENT_SESSIONS).reverse(),
117
+ // Every file's scores for the newest scorecard only: older ones feed the
118
+ // trend, which needs their means alone. Oldest first.
119
+ scorecards: data.scorecards
120
+ .slice(-RECENT_SCORECARDS)
121
+ .map((card, i, all) => (i === all.length - 1 ? card : { ...card, units: [] })),
122
+ metrics: METRICS.map((m) => ({ id: m.id, name: m.name, question: m.question, levels: m.levels })),
123
+ };
124
+ },
58
125
  };
59
126
  }
60
127
  //# sourceMappingURL=snapshot.js.map