@voxslopuli/openagentsy 0.0.0-stage → 0.2.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 (168) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +21 -2
  3. package/dist/adapter-dependency.test.d.ts +2 -0
  4. package/dist/adapter-dependency.test.d.ts.map +1 -0
  5. package/dist/adapter-dependency.test.js +51 -0
  6. package/dist/adapter-dependency.test.js.map +1 -0
  7. package/dist/commands/approve.d.ts +63 -0
  8. package/dist/commands/approve.d.ts.map +1 -0
  9. package/dist/commands/approve.js +218 -0
  10. package/dist/commands/approve.js.map +1 -0
  11. package/dist/commands/approve.test.d.ts +2 -0
  12. package/dist/commands/approve.test.d.ts.map +1 -0
  13. package/dist/commands/approve.test.js +219 -0
  14. package/dist/commands/approve.test.js.map +1 -0
  15. package/dist/commands/audit.d.ts +74 -0
  16. package/dist/commands/audit.d.ts.map +1 -0
  17. package/dist/commands/audit.js +230 -0
  18. package/dist/commands/audit.js.map +1 -0
  19. package/dist/commands/audit.test.d.ts +2 -0
  20. package/dist/commands/audit.test.d.ts.map +1 -0
  21. package/dist/commands/audit.test.js +194 -0
  22. package/dist/commands/audit.test.js.map +1 -0
  23. package/dist/commands/board.d.ts +33 -0
  24. package/dist/commands/board.d.ts.map +1 -0
  25. package/dist/commands/board.js +65 -0
  26. package/dist/commands/board.js.map +1 -0
  27. package/dist/commands/build.d.ts +17 -0
  28. package/dist/commands/build.d.ts.map +1 -0
  29. package/dist/commands/build.js +145 -0
  30. package/dist/commands/build.js.map +1 -0
  31. package/dist/commands/conformance.d.ts +31 -0
  32. package/dist/commands/conformance.d.ts.map +1 -0
  33. package/dist/commands/conformance.js +57 -0
  34. package/dist/commands/conformance.js.map +1 -0
  35. package/dist/commands/conformance.test.d.ts +2 -0
  36. package/dist/commands/conformance.test.d.ts.map +1 -0
  37. package/dist/commands/conformance.test.js +74 -0
  38. package/dist/commands/conformance.test.js.map +1 -0
  39. package/dist/commands/doctor.d.ts +132 -0
  40. package/dist/commands/doctor.d.ts.map +1 -0
  41. package/dist/commands/doctor.js +768 -0
  42. package/dist/commands/doctor.js.map +1 -0
  43. package/dist/commands/doctor.test.d.ts +2 -0
  44. package/dist/commands/doctor.test.d.ts.map +1 -0
  45. package/dist/commands/doctor.test.js +329 -0
  46. package/dist/commands/doctor.test.js.map +1 -0
  47. package/dist/commands/ledger.d.ts +109 -0
  48. package/dist/commands/ledger.d.ts.map +1 -0
  49. package/dist/commands/ledger.js +286 -0
  50. package/dist/commands/ledger.js.map +1 -0
  51. package/dist/commands/ledger.test.d.ts +2 -0
  52. package/dist/commands/ledger.test.d.ts.map +1 -0
  53. package/dist/commands/ledger.test.js +238 -0
  54. package/dist/commands/ledger.test.js.map +1 -0
  55. package/dist/commands/lessons.d.ts +73 -0
  56. package/dist/commands/lessons.d.ts.map +1 -0
  57. package/dist/commands/lessons.js +97 -0
  58. package/dist/commands/lessons.js.map +1 -0
  59. package/dist/commands/lessons.test.d.ts +2 -0
  60. package/dist/commands/lessons.test.d.ts.map +1 -0
  61. package/dist/commands/lessons.test.js +146 -0
  62. package/dist/commands/lessons.test.js.map +1 -0
  63. package/dist/commands/models.d.ts +49 -0
  64. package/dist/commands/models.d.ts.map +1 -0
  65. package/dist/commands/models.js +125 -0
  66. package/dist/commands/models.js.map +1 -0
  67. package/dist/commands/report.d.ts +57 -0
  68. package/dist/commands/report.d.ts.map +1 -0
  69. package/dist/commands/report.js +164 -0
  70. package/dist/commands/report.js.map +1 -0
  71. package/dist/commands/report.test.d.ts +2 -0
  72. package/dist/commands/report.test.d.ts.map +1 -0
  73. package/dist/commands/report.test.js +294 -0
  74. package/dist/commands/report.test.js.map +1 -0
  75. package/dist/commands/scope.d.ts +34 -0
  76. package/dist/commands/scope.d.ts.map +1 -0
  77. package/dist/commands/scope.js +78 -0
  78. package/dist/commands/scope.js.map +1 -0
  79. package/dist/commands/scope.test.d.ts +2 -0
  80. package/dist/commands/scope.test.d.ts.map +1 -0
  81. package/dist/commands/scope.test.js +98 -0
  82. package/dist/commands/scope.test.js.map +1 -0
  83. package/dist/commands/setup.d.ts +31 -0
  84. package/dist/commands/setup.d.ts.map +1 -0
  85. package/dist/commands/setup.js +93 -0
  86. package/dist/commands/setup.js.map +1 -0
  87. package/dist/commands/waivers.d.ts +47 -0
  88. package/dist/commands/waivers.d.ts.map +1 -0
  89. package/dist/commands/waivers.js +68 -0
  90. package/dist/commands/waivers.js.map +1 -0
  91. package/dist/commands/waivers.test.d.ts +2 -0
  92. package/dist/commands/waivers.test.d.ts.map +1 -0
  93. package/dist/commands/waivers.test.js +64 -0
  94. package/dist/commands/waivers.test.js.map +1 -0
  95. package/dist/generate/agent.d.ts +19 -0
  96. package/dist/generate/agent.d.ts.map +1 -0
  97. package/dist/generate/agent.js +35 -0
  98. package/dist/generate/agent.js.map +1 -0
  99. package/dist/generate/agent.test.d.ts +2 -0
  100. package/dist/generate/agent.test.d.ts.map +1 -0
  101. package/dist/generate/agent.test.js +245 -0
  102. package/dist/generate/agent.test.js.map +1 -0
  103. package/dist/generate/commands.d.ts +15 -0
  104. package/dist/generate/commands.d.ts.map +1 -0
  105. package/dist/generate/commands.js +59 -0
  106. package/dist/generate/commands.js.map +1 -0
  107. package/dist/generate/lock.d.ts +18 -0
  108. package/dist/generate/lock.d.ts.map +1 -0
  109. package/dist/generate/lock.js +26 -0
  110. package/dist/generate/lock.js.map +1 -0
  111. package/dist/generate/prompt.d.ts +11 -0
  112. package/dist/generate/prompt.d.ts.map +1 -0
  113. package/dist/generate/prompt.js +114 -0
  114. package/dist/generate/prompt.js.map +1 -0
  115. package/dist/generate/prompt.test.d.ts +2 -0
  116. package/dist/generate/prompt.test.d.ts.map +1 -0
  117. package/dist/generate/prompt.test.js +59 -0
  118. package/dist/generate/prompt.test.js.map +1 -0
  119. package/dist/index.d.ts +40 -0
  120. package/dist/index.d.ts.map +1 -0
  121. package/dist/index.js +448 -0
  122. package/dist/index.js.map +1 -0
  123. package/dist/index.test.d.ts +2 -0
  124. package/dist/index.test.d.ts.map +1 -0
  125. package/dist/index.test.js +237 -0
  126. package/dist/index.test.js.map +1 -0
  127. package/dist/opencode.d.ts +29 -0
  128. package/dist/opencode.d.ts.map +1 -0
  129. package/dist/opencode.js +125 -0
  130. package/dist/opencode.js.map +1 -0
  131. package/dist/plugin-bundle.test.d.ts +2 -0
  132. package/dist/plugin-bundle.test.d.ts.map +1 -0
  133. package/dist/plugin-bundle.test.js +43 -0
  134. package/dist/plugin-bundle.test.js.map +1 -0
  135. package/dist/plugin.d.ts +11 -0
  136. package/dist/plugin.d.ts.map +1 -0
  137. package/dist/plugin.js +46 -0
  138. package/dist/plugin.js.map +1 -0
  139. package/dist/quality-skills.test.d.ts +2 -0
  140. package/dist/quality-skills.test.d.ts.map +1 -0
  141. package/dist/quality-skills.test.js +141 -0
  142. package/dist/quality-skills.test.js.map +1 -0
  143. package/dist/report/footprint.d.ts +12 -0
  144. package/dist/report/footprint.d.ts.map +1 -0
  145. package/dist/report/footprint.js +121 -0
  146. package/dist/report/footprint.js.map +1 -0
  147. package/dist/report/ledger.d.ts +27 -0
  148. package/dist/report/ledger.d.ts.map +1 -0
  149. package/dist/report/ledger.js +197 -0
  150. package/dist/report/ledger.js.map +1 -0
  151. package/dist/report/oversight.d.ts +21 -0
  152. package/dist/report/oversight.d.ts.map +1 -0
  153. package/dist/report/oversight.js +146 -0
  154. package/dist/report/oversight.js.map +1 -0
  155. package/dist/report/policy-firing.test.d.ts +2 -0
  156. package/dist/report/policy-firing.test.d.ts.map +1 -0
  157. package/dist/report/policy-firing.test.js +68 -0
  158. package/dist/report/policy-firing.test.js.map +1 -0
  159. package/dist/report/scope.d.ts +64 -0
  160. package/dist/report/scope.d.ts.map +1 -0
  161. package/dist/report/scope.js +146 -0
  162. package/dist/report/scope.js.map +1 -0
  163. package/dist/who.d.ts +9 -0
  164. package/dist/who.d.ts.map +1 -0
  165. package/dist/who.js +11 -0
  166. package/dist/who.js.map +1 -0
  167. package/package.json +55 -4
  168. package/plugin/openagentsy.js +39478 -0
@@ -0,0 +1,74 @@
1
+ import type { ProjectRoots } from '@openagentsy/core';
2
+ import { type Finding, type SloplintDegradation, type SloplintPath } from '@openagentsy/hooks/quality/audit';
3
+ /**
4
+ * The audit command.
5
+ *
6
+ * `openagentsy audit` reports the quality findings of the change under
7
+ * audit. The scope is the change: the files the working tree differs in
8
+ * from the base, and the files git does not know yet. `--all` audits the
9
+ * whole project instead, and `--base <ref>` measures the change against
10
+ * another ref.
11
+ *
12
+ * The command reads the mode of the project and says what it means: in
13
+ * `after` the hook does nothing and this report is the only one there is,
14
+ * and in `during` the hook reports on every write and this is a snapshot
15
+ * of the scope.
16
+ *
17
+ * The exit code follows the floor rule of the gate: a change with an open
18
+ * floor finding does not pass, and the command says so with a non-zero
19
+ * exit. A change with only reflex findings open passes, because a reflex
20
+ * finding is a tell that a person may waive.
21
+ */
22
+ /** What the audit looks at. */
23
+ export type AuditScope = 'changed' | 'all';
24
+ /** What the caller knows about the audit. */
25
+ export interface AuditOptions {
26
+ /** The scope: the files that changed, or the whole project. */
27
+ scope?: AuditScope | undefined;
28
+ /** The ref the change is measured against. */
29
+ base?: string | undefined;
30
+ }
31
+ /** What the audit found. */
32
+ export interface AuditReport {
33
+ /** The project root. */
34
+ root: string;
35
+ /** The scope that was audited. */
36
+ scope: AuditScope;
37
+ /** The ref the change was measured against. */
38
+ base: string;
39
+ /** When the check runs in this project. */
40
+ mode: 'during' | 'after';
41
+ /** The files in the scope. */
42
+ files: string[];
43
+ /** The files the check read. */
44
+ checked: string[];
45
+ /** Every finding, the waived ones included. */
46
+ findings: Finding[];
47
+ /** The findings no waiver covers, the floor ones first. */
48
+ open: Finding[];
49
+ /** The findings a waiver covers. */
50
+ waived: Finding[];
51
+ /** Which path ran. */
52
+ path: SloplintPath;
53
+ /** Why the linter did not deliver, when it did not. */
54
+ degraded?: SloplintDegradation | undefined;
55
+ /** The open findings of the floor. */
56
+ floor: number;
57
+ /** The open findings of the reflex tier. */
58
+ reflex: number;
59
+ /** The open findings of the taste tier. */
60
+ finish: number;
61
+ /** The exit code the command returns. */
62
+ exitCode: number;
63
+ /** Why the scope is what it is, when the scope is not what was asked for. */
64
+ problem?: string | undefined;
65
+ }
66
+ /**
67
+ * The audit.
68
+ */
69
+ export declare function runAudit(roots: ProjectRoots, options?: AuditOptions): Promise<AuditReport>;
70
+ /**
71
+ * The report, as it is read.
72
+ */
73
+ export declare function formatAuditReport(report: AuditReport): string;
74
+ //# sourceMappingURL=audit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/commands/audit.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EACL,KAAK,OAAO,EAEZ,KAAK,mBAAmB,EACxB,KAAK,YAAY,EAClB,MAAM,kCAAkC,CAAC;AAK1C;;;;;;;;;;;;;;;;;;GAkBG;AAEH,+BAA+B;AAC/B,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,KAAK,CAAC;AAE3C,6CAA6C;AAC7C,MAAM,WAAW,YAAY;IAC3B,+DAA+D;IAC/D,KAAK,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;IAC/B,8CAA8C;IAC9C,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3B;AAED,4BAA4B;AAC5B,MAAM,WAAW,WAAW;IAC1B,wBAAwB;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,kCAAkC;IAClC,KAAK,EAAE,UAAU,CAAC;IAClB,+CAA+C;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,2CAA2C;IAC3C,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC;IACzB,8BAA8B;IAC9B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,gCAAgC;IAChC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,+CAA+C;IAC/C,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,2DAA2D;IAC3D,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,oCAAoC;IACpC,MAAM,EAAE,OAAO,EAAE,CAAC;IAClB,sBAAsB;IACtB,IAAI,EAAE,YAAY,CAAC;IACnB,uDAAuD;IACvD,QAAQ,CAAC,EAAE,mBAAmB,GAAG,SAAS,CAAC;IAC3C,sCAAsC;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,4CAA4C;IAC5C,MAAM,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,yCAAyC;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,6EAA6E;IAC7E,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC9B;AA4CD;;GAEG;AACH,wBAAsB,QAAQ,CAC5B,KAAK,EAAE,YAAY,EACnB,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,WAAW,CAAC,CActB;AA0ID;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAsB7D"}
@@ -0,0 +1,230 @@
1
+ import { readdir, readFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { runQualityAudit, } from '@openagentsy/hooks/quality/audit';
4
+ import { loadWaiverLookup } from '@openagentsy/hooks/quality/waivers';
5
+ import { gitText } from '@openagentsy/ledger';
6
+ import { parse as parseJsonc } from 'jsonc-parser';
7
+ /**
8
+ * The directories the walk does not enter.
9
+ *
10
+ * They hold no source: the built output, the dependencies, the state of
11
+ * the board and the files the build generated.
12
+ */
13
+ const SKIPPED_DIRS = new Set([
14
+ '.git',
15
+ '.openagentsy',
16
+ '.opencode',
17
+ '.turbo',
18
+ 'build',
19
+ 'coverage',
20
+ 'dist',
21
+ 'node_modules',
22
+ 'out',
23
+ 'target',
24
+ 'vendor',
25
+ ]);
26
+ /**
27
+ * The git options the scope reads the repository with.
28
+ *
29
+ * The fsmonitor hook is off: the scope is a list of file names, and a
30
+ * machine whose fsmonitor daemon is not running must still be able to ask
31
+ * what changed. The hook answers with an error, and the audit would report
32
+ * an empty scope instead of the change.
33
+ */
34
+ const GIT_OPTIONS = ['-c', 'core.fsmonitor=false'];
35
+ /** The order the tiers are read in: the floor first, the taste last. */
36
+ const TIER_ORDER = { floor: 0, reflex: 1, finish: 2 };
37
+ /** What each degradation means, in one line. */
38
+ const DEGRADATION_TEXT = {
39
+ absent: 'the Hallmark linter is not installed, so the static rules ran alone',
40
+ 'no-files': 'the scope has no page or stylesheet in it, so the linter had nothing to read',
41
+ crashed: 'the linter failed, so the static findings stand',
42
+ timeout: 'the linter ran too long, so the static findings stand',
43
+ unparsable: 'the linter said something the bridge cannot read, so the static findings stand',
44
+ };
45
+ /**
46
+ * The audit.
47
+ */
48
+ export async function runAudit(roots, options = {}) {
49
+ const { workspace, stateRoot } = roots;
50
+ const scope = options.scope ?? 'changed';
51
+ const base = options.base ?? 'HEAD';
52
+ const resolved = scope === 'all' ? { files: await projectFiles(workspace) } : changedFiles(workspace, base);
53
+ const mode = await readQualityMode(workspace);
54
+ // The waivers are the state root's: a waiver is a decision about the
55
+ // project, and a worktree of the project has the same waivers as the
56
+ // project. The files are the workspace's: they are the files of the
57
+ // branch under audit.
58
+ const waivers = await loadWaiverLookup(stateRoot);
59
+ const result = await runQualityAudit({ root: workspace, files: resolved.files, waivers });
60
+ return reportOf({ root: workspace, scope, base, mode, result, ...resolved });
61
+ }
62
+ /** The report of a run. */
63
+ function reportOf(input) {
64
+ const open = [...input.result.open].sort((a, b) => TIER_ORDER[a.tier] - TIER_ORDER[b.tier]);
65
+ const floor = open.filter((finding) => finding.tier === 'floor').length;
66
+ const reflex = open.filter((finding) => finding.tier === 'reflex').length;
67
+ const finish = open.filter((finding) => finding.tier === 'finish').length;
68
+ return {
69
+ root: input.root,
70
+ scope: input.scope,
71
+ base: input.base,
72
+ mode: input.mode,
73
+ files: input.files,
74
+ checked: input.result.files,
75
+ findings: input.result.findings,
76
+ open,
77
+ waived: input.result.waived,
78
+ path: input.result.path,
79
+ ...(input.result.degraded !== undefined ? { degraded: input.result.degraded } : {}),
80
+ floor,
81
+ reflex,
82
+ finish,
83
+ exitCode: floor > 0 ? 1 : 0,
84
+ ...(input.problem !== undefined ? { problem: input.problem } : {}),
85
+ };
86
+ }
87
+ /**
88
+ * The files that changed.
89
+ *
90
+ * The scope is the change: the files the working tree differs in from the
91
+ * base, and the files git does not know yet. A directory that is not a
92
+ * repository has no changed files, and the report says so.
93
+ */
94
+ function changedFiles(root, base) {
95
+ const inside = gitText(root, ['rev-parse', '--is-inside-work-tree']);
96
+ if (inside === null || inside.trim() !== 'true') {
97
+ return { files: [], problem: 'this directory is not a git repository, so the scope is empty' };
98
+ }
99
+ const diff = gitText(root, [...GIT_OPTIONS, 'diff', '--name-only', '--relative', base]);
100
+ if (diff === null) {
101
+ return { files: [], problem: `the base ${base} could not be read, so the scope is empty` };
102
+ }
103
+ const untracked = gitText(root, [...GIT_OPTIONS, 'ls-files', '--others', '--exclude-standard']);
104
+ return { files: unique([...lines(diff), ...lines(untracked ?? '')]) };
105
+ }
106
+ /**
107
+ * The files of the whole project.
108
+ *
109
+ * The walk skips the directories that hold no source. What it finds is
110
+ * every file in the project, and the check reads the ones a rule applies
111
+ * to.
112
+ */
113
+ async function projectFiles(root) {
114
+ const files = [];
115
+ await walk(root, '', files);
116
+ return files;
117
+ }
118
+ /** The files of a directory, and of the directories under it. */
119
+ async function walk(root, dir, files) {
120
+ const entries = await entriesOf(join(root, dir));
121
+ for (const entry of entries) {
122
+ const path = dir === '' ? entry.name : `${dir}/${entry.name}`;
123
+ if (entry.isDirectory()) {
124
+ if (SKIPPED_DIRS.has(entry.name))
125
+ continue;
126
+ await walk(root, path, files);
127
+ continue;
128
+ }
129
+ if (entry.isFile())
130
+ files.push(path);
131
+ }
132
+ }
133
+ /** The entries of a directory, when it can be read. */
134
+ async function entriesOf(path) {
135
+ try {
136
+ return await readdir(path, { withFileTypes: true });
137
+ }
138
+ catch {
139
+ return [];
140
+ }
141
+ }
142
+ /** The paths of a git answer, one per line. */
143
+ function lines(text) {
144
+ return text
145
+ .split('\n')
146
+ .map((line) => line.trim())
147
+ .filter((line) => line !== '');
148
+ }
149
+ /** The paths, without the ones that came twice. */
150
+ function unique(paths) {
151
+ return [...new Set(paths)];
152
+ }
153
+ /**
154
+ * The quality mode of the project.
155
+ *
156
+ * The mode says when the check runs: `during` runs it after every write,
157
+ * and `after` leaves the findings to this command. A project that names no
158
+ * mode runs the check after every write, which is the default.
159
+ */
160
+ async function readQualityMode(root) {
161
+ const config = await readJsoncFile(join(root, 'openagentsy.jsonc'));
162
+ const quality = asRecord(config?.quality);
163
+ const mode = quality?.mode;
164
+ return mode === 'after' ? 'after' : 'during';
165
+ }
166
+ /** A JSONC file, read the way a person writes one: with comments. */
167
+ async function readJsoncFile(path) {
168
+ try {
169
+ const text = await readFile(path, 'utf8');
170
+ return asRecord(parseJsonc(text));
171
+ }
172
+ catch {
173
+ return undefined;
174
+ }
175
+ }
176
+ /** A value, when it is an object. */
177
+ function asRecord(value) {
178
+ if (typeof value !== 'object' || value === null || Array.isArray(value))
179
+ return undefined;
180
+ return value;
181
+ }
182
+ /**
183
+ * The report, as it is read.
184
+ */
185
+ export function formatAuditReport(report) {
186
+ const lines = [];
187
+ lines.push(`Audit of ${report.files.length} ${scopeWord(report.scope)} (${report.checked.length} checked)`);
188
+ lines.push(` path ${report.path} — ${DEGRADATION_TEXT[report.degraded ?? 'absent']}`);
189
+ lines.push(` mode ${report.mode} — ${modeText(report.mode)}`);
190
+ lines.push(` open ${report.floor} floor, ${report.reflex} reflex, ${report.finish} taste`);
191
+ lines.push(` waived ${report.waived.length}`);
192
+ if (report.problem !== undefined)
193
+ lines.push(` ${report.problem}`);
194
+ lines.push('');
195
+ for (const finding of report.open)
196
+ lines.push(findingLine(finding.tier, finding));
197
+ for (const finding of report.waived)
198
+ lines.push(findingLine('waived', finding));
199
+ if (report.findings.length === 0) {
200
+ lines.push('no findings: the scope is clean');
201
+ lines.push('');
202
+ }
203
+ lines.push(verdictText(report));
204
+ if (report.reflex > 0) {
205
+ lines.push(`${report.reflex} open reflex findings: fix them or waive them`);
206
+ }
207
+ return `${lines.join('\n')}\n`;
208
+ }
209
+ /** The word for the scope. */
210
+ function scopeWord(scope) {
211
+ return scope === 'all' ? 'files in the project' : 'changed files';
212
+ }
213
+ /** The mode, and what it means for this report. */
214
+ function modeText(mode) {
215
+ if (mode === 'after')
216
+ return 'the hook is off, so this report is the only one there is';
217
+ return 'the hook reports on every write; this report is a snapshot of the scope';
218
+ }
219
+ /** One finding, one line. */
220
+ function findingLine(tier, finding) {
221
+ return ` ${tier.padEnd(6)} ${finding.ruleId.padEnd(24)} ${finding.file}:${finding.line} ${finding.fix}`;
222
+ }
223
+ /** The verdict of the floor rule. */
224
+ function verdictText(report) {
225
+ if (report.floor > 0) {
226
+ return `${report.floor} open floor findings: the change does not pass the gate`;
227
+ }
228
+ return 'no open floor findings: the change passes the gate';
229
+ }
230
+ //# sourceMappingURL=audit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/commands/audit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=audit.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.test.d.ts","sourceRoot":"","sources":["../../src/commands/audit.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,194 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
3
+ import { tmpdir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ import { promisify } from 'node:util';
6
+ import { describe, expect, it } from 'vitest';
7
+ import { formatAuditReport, runAudit } from './audit.js';
8
+ import { runWaiver } from './waivers.js';
9
+ const exec = promisify(execFile);
10
+ /** The two roots of a project that is its own state root. */
11
+ function rootsOf(root) {
12
+ return { workspace: root, stateRoot: root };
13
+ }
14
+ /** A stylesheet with a floor finding: the page root clips its overflow. */
15
+ const FLOOR_CSS = 'html {\n overflow-x: hidden;\n}\n';
16
+ /** A stylesheet with a reflex finding: a colour written out instead of a token. */
17
+ const REFLEX_CSS = '.chip {\n color: #ff0000;\n}\n';
18
+ /** A project to audit: a directory with a source directory in it. */
19
+ async function project() {
20
+ const root = await mkdtemp(join(tmpdir(), 'openagentsy-cli-audit-'));
21
+ await mkdir(join(root, 'src'), { recursive: true });
22
+ return { root, remove: () => rm(root, { recursive: true, force: true }) };
23
+ }
24
+ /**
25
+ * The git variables that point a command at a repository.
26
+ *
27
+ * A git that runs a hook sets these in the environment of the hook, and a
28
+ * child inherits them: the child then addresses the repository of the hook
29
+ * instead of the one it was asked about, and a test that writes into the
30
+ * repository of the hook writes into the project. The variables are
31
+ * removed, so the repository is the one the test named.
32
+ */
33
+ const GIT_REPOSITORY_VARS = [
34
+ 'GIT_DIR',
35
+ 'GIT_WORK_TREE',
36
+ 'GIT_INDEX_FILE',
37
+ 'GIT_COMMON_DIR',
38
+ 'GIT_OBJECT_DIRECTORY',
39
+ 'GIT_ALTERNATE_OBJECT_DIRECTORIES',
40
+ 'GIT_CEILING_DIRECTORIES',
41
+ 'GIT_PREFIX',
42
+ 'GIT_NAMESPACE',
43
+ ];
44
+ /** The environment of a git child: the caller's, without the git variables. */
45
+ function cleanEnv() {
46
+ const env = {};
47
+ for (const [key, value] of Object.entries(process.env)) {
48
+ if (value === undefined)
49
+ continue;
50
+ if (GIT_REPOSITORY_VARS.includes(key))
51
+ continue;
52
+ env[key] = value;
53
+ }
54
+ return env;
55
+ }
56
+ /**
57
+ * The git of a test project.
58
+ *
59
+ * The fsmonitor hook is off, as it is in the command: the hook of a
60
+ * machine whose daemon is not running answers with an error, and the
61
+ * scope would come back empty.
62
+ *
63
+ * The signing is off too, and for the same kind of reason: a machine whose
64
+ * global config asks for a signature would need a signing agent to answer,
65
+ * and a test that needs an agent to answer is a test that fails on a
66
+ * machine without one. A test signs nothing.
67
+ */
68
+ async function git(root, args) {
69
+ await exec('git', ['-c', 'core.fsmonitor=false', '-c', 'commit.gpgsign=false', ...args], {
70
+ cwd: root,
71
+ env: cleanEnv(),
72
+ });
73
+ }
74
+ /** A project with a commit in it, so the changed scope has a base. */
75
+ async function repository() {
76
+ const { root } = await project();
77
+ await git(root, ['init', '-q']);
78
+ await git(root, [
79
+ '-c',
80
+ 'user.email=test@example.com',
81
+ '-c',
82
+ 'user.name=Test',
83
+ 'commit',
84
+ '-q',
85
+ '--allow-empty',
86
+ '-m',
87
+ 'base',
88
+ ]);
89
+ return root;
90
+ }
91
+ describe('the audit command', () => {
92
+ it('lists the findings of the scope', async () => {
93
+ const root = await repository();
94
+ try {
95
+ await writeFile(join(root, 'src/app.css'), FLOOR_CSS);
96
+ await git(root, ['add', 'src/app.css']);
97
+ const report = await runAudit(rootsOf(root));
98
+ expect(report.scope).toBe('changed');
99
+ expect(report.files).toContain('src/app.css');
100
+ expect(report.floor).toBe(1);
101
+ const text = formatAuditReport(report);
102
+ expect(text).toContain('css/overflow-clip');
103
+ expect(text).toContain('src/app.css:2');
104
+ }
105
+ finally {
106
+ await rm(root, { recursive: true, force: true });
107
+ }
108
+ });
109
+ it('prints the path that ran', async () => {
110
+ const { root, remove } = await project();
111
+ try {
112
+ await writeFile(join(root, 'src/app.ts'), 'export const value = 1;\n');
113
+ const report = await runAudit(rootsOf(root), { scope: 'all' });
114
+ // The scope holds no page and no stylesheet, so the linter had
115
+ // nothing to read and the static rules ran alone.
116
+ expect(report.path).toBe('static');
117
+ expect(report.degraded).toBe('no-files');
118
+ expect(formatAuditReport(report)).toContain('static');
119
+ }
120
+ finally {
121
+ await remove();
122
+ }
123
+ });
124
+ it('a waived finding is not counted as open', async () => {
125
+ const { root, remove } = await project();
126
+ try {
127
+ await writeFile(join(root, 'src/theme.css'), REFLEX_CSS);
128
+ const waiver = await runWaiver(root, 'css/token-only-colour-font', 'src/theme.css', 'The file is a fixture, not a page.');
129
+ expect(waiver.ok).toBe(true);
130
+ const report = await runAudit(rootsOf(root), { scope: 'all' });
131
+ expect(report.reflex).toBe(0);
132
+ expect(report.waived.length).toBe(1);
133
+ expect(report.waived[0]?.ruleId).toBe('css/token-only-colour-font');
134
+ expect(report.exitCode).toBe(0);
135
+ expect(formatAuditReport(report)).toContain('waived');
136
+ }
137
+ finally {
138
+ await remove();
139
+ }
140
+ });
141
+ it('the exit code follows the floor rule', async () => {
142
+ const { root, remove } = await project();
143
+ try {
144
+ await writeFile(join(root, 'src/app.css'), FLOOR_CSS);
145
+ await writeFile(join(root, 'src/theme.css'), REFLEX_CSS);
146
+ const report = await runAudit(rootsOf(root), { scope: 'all' });
147
+ expect(report.floor).toBe(1);
148
+ expect(report.reflex).toBe(1);
149
+ expect(report.exitCode).toBe(1);
150
+ }
151
+ finally {
152
+ await remove();
153
+ }
154
+ });
155
+ it('passes when only reflex findings are open', async () => {
156
+ const { root, remove } = await project();
157
+ try {
158
+ await writeFile(join(root, 'src/theme.css'), REFLEX_CSS);
159
+ const report = await runAudit(rootsOf(root), { scope: 'all' });
160
+ expect(report.floor).toBe(0);
161
+ expect(report.reflex).toBe(1);
162
+ expect(report.exitCode).toBe(0);
163
+ }
164
+ finally {
165
+ await remove();
166
+ }
167
+ });
168
+ it('reads the after mode', async () => {
169
+ const { root, remove } = await project();
170
+ try {
171
+ await writeFile(join(root, 'openagentsy.jsonc'), '{\n // the check runs on the command, not on every write\n "quality": { "mode": "after" }\n}\n');
172
+ const report = await runAudit(rootsOf(root), { scope: 'all' });
173
+ expect(report.mode).toBe('after');
174
+ const text = formatAuditReport(report);
175
+ expect(text).toContain('after');
176
+ expect(text).toContain('the hook is off');
177
+ }
178
+ finally {
179
+ await remove();
180
+ }
181
+ });
182
+ it('runs the check on every write by default', async () => {
183
+ const { root, remove } = await project();
184
+ try {
185
+ const report = await runAudit(rootsOf(root), { scope: 'all' });
186
+ expect(report.mode).toBe('during');
187
+ expect(formatAuditReport(report)).toContain('during');
188
+ }
189
+ finally {
190
+ await remove();
191
+ }
192
+ });
193
+ });
194
+ //# sourceMappingURL=audit.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.test.js","sourceRoot":"","sources":["../../src/commands/audit.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * The board, as a person reads it.
3
+ *
4
+ * The plan asks for a read-only view so a human can follow a run along:
5
+ * the records the agents left, the leases they hold, and the budgets they
6
+ * spend. It is a command and not a web page, because the thing it has to
7
+ * do is show what happened, and a page is a server to keep alive.
8
+ *
9
+ * The board is the state root's, so a command run in a worktree shows the
10
+ * board of the project and not a board of its own.
11
+ */
12
+ export type BoardView = {
13
+ records: {
14
+ id: number;
15
+ kind: string;
16
+ from: string;
17
+ bean: string;
18
+ action: string;
19
+ }[];
20
+ leases: {
21
+ bean: string;
22
+ agent: string;
23
+ glob: string;
24
+ }[];
25
+ budgets: {
26
+ bean: string;
27
+ tokenLimit: number;
28
+ stopAt: number;
29
+ }[];
30
+ };
31
+ export declare function runBoard(stateRoot: string): Promise<BoardView>;
32
+ export declare function formatBoard(view: BoardView): string;
33
+ //# sourceMappingURL=board.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"board.d.ts","sourceRoot":"","sources":["../../src/commands/board.ts"],"names":[],"mappings":"AAUA;;;;;;;;;;GAUG;AAEH,MAAM,MAAM,SAAS,GAAG;IACtB,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACpF,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACxD,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACjE,CAAC;AAEF,wBAAsB,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAmCpE;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAkCnD"}
@@ -0,0 +1,65 @@
1
+ import { access } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { closeDatabase, listBudgets, listLeases, listRecords, openDatabase, } from '@openagentsy/core';
4
+ export async function runBoard(stateRoot) {
5
+ const databasePath = join(stateRoot, '.openagentsy', 'board.sqlite');
6
+ // A project that has no board yet has nothing to show, and reading it
7
+ // should not create one.
8
+ const exists = await access(databasePath).then(() => true, () => false);
9
+ if (!exists)
10
+ return { records: [], leases: [], budgets: [] };
11
+ const db = openDatabase(databasePath);
12
+ try {
13
+ const inbox = listRecords(db, { limit: 50 });
14
+ return {
15
+ records: inbox.records.map((record) => ({
16
+ id: record.id,
17
+ kind: record.kind,
18
+ from: record.from,
19
+ bean: record.bean,
20
+ action: record.action,
21
+ })),
22
+ leases: listLeases(db).map((lease) => ({
23
+ bean: lease.bean,
24
+ agent: lease.agent,
25
+ glob: lease.glob,
26
+ })),
27
+ budgets: listBudgets(db).map((budget) => ({
28
+ bean: budget.bean,
29
+ tokenLimit: budget.tokenLimit,
30
+ stopAt: budget.stopAt,
31
+ })),
32
+ };
33
+ }
34
+ finally {
35
+ closeDatabase(db);
36
+ }
37
+ }
38
+ export function formatBoard(view) {
39
+ const lines = [];
40
+ lines.push('records');
41
+ if (view.records.length === 0) {
42
+ lines.push(' (none)');
43
+ }
44
+ for (const record of view.records) {
45
+ lines.push(` ${String(record.id).padStart(4)} ${record.kind.padEnd(9)} ${record.from.padEnd(12)} ${record.bean.padEnd(14)} ${record.action}`);
46
+ }
47
+ lines.push('');
48
+ lines.push('leases');
49
+ if (view.leases.length === 0) {
50
+ lines.push(' (none)');
51
+ }
52
+ for (const lease of view.leases) {
53
+ lines.push(` ${lease.agent.padEnd(14)} ${lease.glob.padEnd(30)} ${lease.bean}`);
54
+ }
55
+ lines.push('');
56
+ lines.push('budgets');
57
+ if (view.budgets.length === 0) {
58
+ lines.push(' (none)');
59
+ }
60
+ for (const budget of view.budgets) {
61
+ lines.push(` ${budget.bean.padEnd(20)} ${budget.tokenLimit} tokens, stops at ${budget.stopAt}`);
62
+ }
63
+ return lines.join('\n');
64
+ }
65
+ //# sourceMappingURL=board.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"board.js","sourceRoot":"","sources":["../../src/commands/board.ts"],"names":[],"mappings":"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"}