devharness 0.9.4 → 0.9.6

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 (105) hide show
  1. package/build/command-recorder.d.ts +24 -0
  2. package/build/command-recorder.d.ts.map +1 -1
  3. package/build/command-recorder.js +55 -8
  4. package/build/command-recorder.js.map +1 -1
  5. package/build/config.d.ts +9 -0
  6. package/build/config.d.ts.map +1 -1
  7. package/build/config.js +10 -0
  8. package/build/config.js.map +1 -1
  9. package/build/dashboard/dashboard-hub.js +2 -2
  10. package/build/dashboard/dashboard-hub.js.map +1 -1
  11. package/build/github/gh-cli.d.ts +41 -0
  12. package/build/github/gh-cli.d.ts.map +1 -0
  13. package/build/github/gh-cli.js +147 -0
  14. package/build/github/gh-cli.js.map +1 -0
  15. package/build/github/gh-cli.test.d.ts +2 -0
  16. package/build/github/gh-cli.test.d.ts.map +1 -0
  17. package/build/github/gh-cli.test.js +145 -0
  18. package/build/github/gh-cli.test.js.map +1 -0
  19. package/build/github/gh-issues.d.ts +105 -0
  20. package/build/github/gh-issues.d.ts.map +1 -0
  21. package/build/github/gh-issues.js +199 -0
  22. package/build/github/gh-issues.js.map +1 -0
  23. package/build/github/gh-issues.test.d.ts +2 -0
  24. package/build/github/gh-issues.test.d.ts.map +1 -0
  25. package/build/github/gh-issues.test.js +118 -0
  26. package/build/github/gh-issues.test.js.map +1 -0
  27. package/build/github/issue-actions.d.ts +34 -0
  28. package/build/github/issue-actions.d.ts.map +1 -0
  29. package/build/github/issue-actions.js +530 -0
  30. package/build/github/issue-actions.js.map +1 -0
  31. package/build/github/issue-actions.test.d.ts +2 -0
  32. package/build/github/issue-actions.test.d.ts.map +1 -0
  33. package/build/github/issue-actions.test.js +555 -0
  34. package/build/github/issue-actions.test.js.map +1 -0
  35. package/build/github/issue-body.d.ts +179 -0
  36. package/build/github/issue-body.d.ts.map +1 -0
  37. package/build/github/issue-body.js +181 -0
  38. package/build/github/issue-body.js.map +1 -0
  39. package/build/github/issue-body.test.d.ts +2 -0
  40. package/build/github/issue-body.test.d.ts.map +1 -0
  41. package/build/github/issue-body.test.js +157 -0
  42. package/build/github/issue-body.test.js.map +1 -0
  43. package/build/helpers/paths.d.ts +7 -0
  44. package/build/helpers/paths.d.ts.map +1 -1
  45. package/build/helpers/paths.js +10 -1
  46. package/build/helpers/paths.js.map +1 -1
  47. package/build/index.js +7 -1
  48. package/build/index.js.map +1 -1
  49. package/build/issue-tracker.d.ts +30 -0
  50. package/build/issue-tracker.d.ts.map +1 -1
  51. package/build/issue-tracker.js +91 -1
  52. package/build/issue-tracker.js.map +1 -1
  53. package/build/issue-tracker.test.js +76 -1
  54. package/build/issue-tracker.test.js.map +1 -1
  55. package/build/tool-response.d.ts +34 -0
  56. package/build/tool-response.d.ts.map +1 -1
  57. package/build/tool-response.js.map +1 -1
  58. package/build/tools/assert-tools.d.ts +6 -6
  59. package/build/tools/breakpoint-tools.d.ts +3 -3
  60. package/build/tools/click-validation-console-noise.test.d.ts +2 -0
  61. package/build/tools/click-validation-console-noise.test.d.ts.map +1 -0
  62. package/build/tools/click-validation-console-noise.test.js +103 -0
  63. package/build/tools/click-validation-console-noise.test.js.map +1 -0
  64. package/build/tools/console-tools.d.ts +6 -6
  65. package/build/tools/console-tools.d.ts.map +1 -1
  66. package/build/tools/console-tools.js +1 -0
  67. package/build/tools/console-tools.js.map +1 -1
  68. package/build/tools/execution-tools.d.ts +3 -3
  69. package/build/tools/input-tools.d.ts +3 -3
  70. package/build/tools/inspection-tools.d.ts +15 -15
  71. package/build/tools/interpolation-reverse.d.ts +26 -0
  72. package/build/tools/interpolation-reverse.d.ts.map +1 -0
  73. package/build/tools/interpolation-reverse.js +130 -0
  74. package/build/tools/interpolation-reverse.js.map +1 -0
  75. package/build/tools/interpolation-reverse.test.d.ts +2 -0
  76. package/build/tools/interpolation-reverse.test.d.ts.map +1 -0
  77. package/build/tools/interpolation-reverse.test.js +76 -0
  78. package/build/tools/interpolation-reverse.test.js.map +1 -0
  79. package/build/tools/issues-tools.d.ts +29 -5
  80. package/build/tools/issues-tools.d.ts.map +1 -1
  81. package/build/tools/issues-tools.js +55 -5
  82. package/build/tools/issues-tools.js.map +1 -1
  83. package/build/tools/network-tools.d.ts +9 -9
  84. package/build/tools/replay-create-templatize.test.d.ts +2 -0
  85. package/build/tools/replay-create-templatize.test.d.ts.map +1 -0
  86. package/build/tools/replay-create-templatize.test.js +103 -0
  87. package/build/tools/replay-create-templatize.test.js.map +1 -0
  88. package/build/tools/replay-executor.d.ts +4 -0
  89. package/build/tools/replay-executor.d.ts.map +1 -1
  90. package/build/tools/replay-executor.js +39 -10
  91. package/build/tools/replay-executor.js.map +1 -1
  92. package/build/tools/replay-sequence-reload.test.js +23 -7
  93. package/build/tools/replay-sequence-reload.test.js.map +1 -1
  94. package/build/tools/replay-tools.d.ts +18 -18
  95. package/build/tools/replay-tools.d.ts.map +1 -1
  96. package/build/tools/replay-tools.js +6 -5
  97. package/build/tools/replay-tools.js.map +1 -1
  98. package/build/tools/request-tools.d.ts +1 -1
  99. package/build/tools/storage-tools.d.ts +6 -6
  100. package/docs/instructions.md +3 -1
  101. package/docs/messages.md +452 -1
  102. package/package.json +1 -1
  103. package/plugin/skills/devharness/SKILL.md +1 -1
  104. package/plugin/skills/devharness/references/sequences.md +6 -1
  105. package/plugin/skills/devharness/references/tool-categories.md +8 -1
@@ -0,0 +1,179 @@
1
+ /**
2
+ * The issue body as a transport for sequences.
3
+ *
4
+ * A sequence rides inside the body as a fenced block tagged
5
+ * ```json devharness-sequence. GitHub highlights on the first word of the
6
+ * info string, so it renders as JSON and the second word is ours to find.
7
+ *
8
+ * Everything here treats the markdown as untrusted: issue bodies already
9
+ * contain code fences of their own, and a sequence pulled from a public issue
10
+ * is a script, not a macro.
11
+ */
12
+ import { z } from 'zod';
13
+ export declare const SEQUENCE_FENCE_INFO = "json devharness-sequence";
14
+ /** Tools that do more than drive a page, so a remote sequence using one is
15
+ * refused unless the caller explicitly opts in. */
16
+ export declare const PRIVILEGED_TOOLS: ReadonlySet<string>;
17
+ export interface FencedBlock {
18
+ /** The info string, e.g. `json devharness-sequence`. */
19
+ info: string;
20
+ content: string;
21
+ /** 0-based line of the opening fence. */
22
+ startLine: number;
23
+ }
24
+ /**
25
+ * Every fenced block, in document order.
26
+ *
27
+ * Tracks the opening fence's character and length so a longer fence can
28
+ * legally contain shorter ones - the reason this is a scanner and not a
29
+ * regex. Ten issue files on disk already carry code fences of their own.
30
+ */
31
+ export declare function extractFencedBlocks(markdown: string): FencedBlock[];
32
+ export declare function findSequenceBlocks(markdown: string): FencedBlock[];
33
+ /** The first sequence block. First, not newest - order in the body is the rule. */
34
+ export declare function findSequenceBlock(markdown: string): FencedBlock | null;
35
+ /**
36
+ * Emit a sequence as a fence wide enough to contain it. A step can be an
37
+ * `execution` call carrying a JS template literal, so three backticks is not
38
+ * always enough; GitHub renders a wider fence identically.
39
+ */
40
+ export declare function emitSequenceBlock(sequence: unknown): string;
41
+ /**
42
+ * Remove sequence blocks and collapse the gap they leave.
43
+ *
44
+ * Used before hashing and before writing a pulled body: the block is a
45
+ * projection of the sequence file, not body content, so leaving it in would
46
+ * make every publish look like a local edit.
47
+ */
48
+ export declare function stripSequenceBlocks(markdown: string): string;
49
+ /** Deliberately a whitelist with .strip(): unknown keys from a public issue
50
+ * are dropped rather than carried onto disk. `id` is regenerated locally. */
51
+ declare const remoteSequenceSchema: z.ZodObject<{
52
+ name: z.ZodOptional<z.ZodString>;
53
+ description: z.ZodOptional<z.ZodString>;
54
+ expectedOutcome: z.ZodOptional<z.ZodString>;
55
+ startUrl: z.ZodOptional<z.ZodString>;
56
+ commands: z.ZodArray<z.ZodObject<{
57
+ tool: z.ZodString;
58
+ params: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
59
+ delay: z.ZodOptional<z.ZodNumber>;
60
+ comment: z.ZodOptional<z.ZodString>;
61
+ }, "strip", z.ZodTypeAny, {
62
+ tool: string;
63
+ params?: Record<string, any> | undefined;
64
+ delay?: number | undefined;
65
+ comment?: string | undefined;
66
+ }, {
67
+ tool: string;
68
+ params?: Record<string, any> | undefined;
69
+ delay?: number | undefined;
70
+ comment?: string | undefined;
71
+ }>, "many">;
72
+ teardown: z.ZodOptional<z.ZodArray<z.ZodObject<{
73
+ tool: z.ZodString;
74
+ params: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
75
+ delay: z.ZodOptional<z.ZodNumber>;
76
+ comment: z.ZodOptional<z.ZodString>;
77
+ }, "strip", z.ZodTypeAny, {
78
+ tool: string;
79
+ params?: Record<string, any> | undefined;
80
+ delay?: number | undefined;
81
+ comment?: string | undefined;
82
+ }, {
83
+ tool: string;
84
+ params?: Record<string, any> | undefined;
85
+ delay?: number | undefined;
86
+ comment?: string | undefined;
87
+ }>, "many">>;
88
+ tags: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
89
+ requiredSockets: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
90
+ requiredConnections: z.ZodOptional<z.ZodArray<z.ZodObject<{
91
+ reference: z.ZodString;
92
+ url: z.ZodOptional<z.ZodString>;
93
+ profile: z.ZodOptional<z.ZodString>;
94
+ forceNewInstance: z.ZodOptional<z.ZodBoolean>;
95
+ role: z.ZodOptional<z.ZodString>;
96
+ }, "strip", z.ZodTypeAny, {
97
+ reference: string;
98
+ profile?: string | undefined;
99
+ url?: string | undefined;
100
+ forceNewInstance?: boolean | undefined;
101
+ role?: string | undefined;
102
+ }, {
103
+ reference: string;
104
+ profile?: string | undefined;
105
+ url?: string | undefined;
106
+ forceNewInstance?: boolean | undefined;
107
+ role?: string | undefined;
108
+ }>, "many">>;
109
+ }, "strip", z.ZodTypeAny, {
110
+ commands: {
111
+ tool: string;
112
+ params?: Record<string, any> | undefined;
113
+ delay?: number | undefined;
114
+ comment?: string | undefined;
115
+ }[];
116
+ name?: string | undefined;
117
+ startUrl?: string | undefined;
118
+ description?: string | undefined;
119
+ expectedOutcome?: string | undefined;
120
+ requiredConnections?: {
121
+ reference: string;
122
+ profile?: string | undefined;
123
+ url?: string | undefined;
124
+ forceNewInstance?: boolean | undefined;
125
+ role?: string | undefined;
126
+ }[] | undefined;
127
+ tags?: string[] | undefined;
128
+ requiredSockets?: string[] | undefined;
129
+ teardown?: {
130
+ tool: string;
131
+ params?: Record<string, any> | undefined;
132
+ delay?: number | undefined;
133
+ comment?: string | undefined;
134
+ }[] | undefined;
135
+ }, {
136
+ commands: {
137
+ tool: string;
138
+ params?: Record<string, any> | undefined;
139
+ delay?: number | undefined;
140
+ comment?: string | undefined;
141
+ }[];
142
+ name?: string | undefined;
143
+ startUrl?: string | undefined;
144
+ description?: string | undefined;
145
+ expectedOutcome?: string | undefined;
146
+ requiredConnections?: {
147
+ reference: string;
148
+ profile?: string | undefined;
149
+ url?: string | undefined;
150
+ forceNewInstance?: boolean | undefined;
151
+ role?: string | undefined;
152
+ }[] | undefined;
153
+ tags?: string[] | undefined;
154
+ requiredSockets?: string[] | undefined;
155
+ teardown?: {
156
+ tool: string;
157
+ params?: Record<string, any> | undefined;
158
+ delay?: number | undefined;
159
+ comment?: string | undefined;
160
+ }[] | undefined;
161
+ }>;
162
+ export type RemoteSequence = z.infer<typeof remoteSequenceSchema>;
163
+ export type ParseRemoteSequenceResult = {
164
+ ok: true;
165
+ sequence: RemoteSequence;
166
+ } | {
167
+ ok: false;
168
+ reason: string;
169
+ };
170
+ export declare function parseRemoteSequence(json: string): ParseRemoteSequenceResult;
171
+ export interface SequenceAudit {
172
+ steps: number;
173
+ tools: string[];
174
+ privileged: string[];
175
+ }
176
+ /** What this sequence would do, in the terms a reader needs before running it. */
177
+ export declare function auditSequence(sequence: RemoteSequence): SequenceAudit;
178
+ export {};
179
+ //# sourceMappingURL=issue-body.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"issue-body.d.ts","sourceRoot":"","sources":["../../src/github/issue-body.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,mBAAmB,6BAA6B,CAAC;AAK9D;oDACoD;AACpD,eAAO,MAAM,gBAAgB,EAAE,WAAW,CAAC,MAAM,CAE/C,CAAC;AAEH,MAAM,WAAW,WAAW;IAC1B,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,yCAAyC;IACzC,SAAS,EAAE,MAAM,CAAC;CACnB;AAID;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,EAAE,CAoCnE;AAOD,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,EAAE,CAElE;AAED,mFAAmF;AACnF,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAEtE;AAYD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,CAI3D;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAiB5D;AAaD;8EAC8E;AAC9E,QAAA,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgBhB,CAAC;AAEX,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE,MAAM,MAAM,yBAAyB,GACjC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,cAAc,CAAA;CAAE,GACtC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAElC,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,yBAAyB,CAoB3E;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,kFAAkF;AAClF,wBAAgB,aAAa,CAAC,QAAQ,EAAE,cAAc,GAAG,aAAa,CAQrE"}
@@ -0,0 +1,181 @@
1
+ /**
2
+ * The issue body as a transport for sequences.
3
+ *
4
+ * A sequence rides inside the body as a fenced block tagged
5
+ * ```json devharness-sequence. GitHub highlights on the first word of the
6
+ * info string, so it renders as JSON and the second word is ours to find.
7
+ *
8
+ * Everything here treats the markdown as untrusted: issue bodies already
9
+ * contain code fences of their own, and a sequence pulled from a public issue
10
+ * is a script, not a macro.
11
+ */
12
+ import { z } from 'zod';
13
+ export const SEQUENCE_FENCE_INFO = 'json devharness-sequence';
14
+ const SEQUENCE_TAG = 'devharness-sequence';
15
+ const MAX_SEQUENCE_BYTES = 256 * 1024;
16
+ const MAX_STEPS = 500;
17
+ /** Tools that do more than drive a page, so a remote sequence using one is
18
+ * refused unless the caller explicitly opts in. */
19
+ export const PRIVILEGED_TOOLS = new Set([
20
+ 'execution', 'saveToDisk', 'server', 'request', 'download',
21
+ ]);
22
+ const FENCE_RE = /^ {0,3}(`{3,}|~{3,})(.*)$/;
23
+ /**
24
+ * Every fenced block, in document order.
25
+ *
26
+ * Tracks the opening fence's character and length so a longer fence can
27
+ * legally contain shorter ones - the reason this is a scanner and not a
28
+ * regex. Ten issue files on disk already carry code fences of their own.
29
+ */
30
+ export function extractFencedBlocks(markdown) {
31
+ const lines = markdown.split(/\r?\n/);
32
+ const blocks = [];
33
+ let open = null;
34
+ for (let i = 0; i < lines.length; i++) {
35
+ const match = lines[i].match(FENCE_RE);
36
+ if (!open) {
37
+ if (!match)
38
+ continue;
39
+ // A backtick info string may not contain backticks (CommonMark), which
40
+ // is what stops ```` ```a```b ```` opening a block.
41
+ const info = match[2];
42
+ if (match[1][0] === '`' && info.includes('`'))
43
+ continue;
44
+ open = { char: match[1][0], length: match[1].length, info: info.trim(), startLine: i, body: [] };
45
+ continue;
46
+ }
47
+ const closes = match
48
+ && match[1][0] === open.char
49
+ && match[1].length >= open.length
50
+ && match[2].trim() === '';
51
+ if (closes) {
52
+ blocks.push({ info: open.info, content: open.body.join('\n'), startLine: open.startLine });
53
+ open = null;
54
+ }
55
+ else {
56
+ open.body.push(lines[i]);
57
+ }
58
+ }
59
+ // An unterminated fence still holds a block - GitHub renders it that way.
60
+ if (open)
61
+ blocks.push({ info: open.info, content: open.body.join('\n'), startLine: open.startLine });
62
+ return blocks;
63
+ }
64
+ function isSequenceBlock(block) {
65
+ const words = block.info.split(/\s+/).filter(Boolean);
66
+ return words.length >= 2 && words[0] === 'json' && words[1] === SEQUENCE_TAG;
67
+ }
68
+ export function findSequenceBlocks(markdown) {
69
+ return extractFencedBlocks(markdown).filter(isSequenceBlock);
70
+ }
71
+ /** The first sequence block. First, not newest - order in the body is the rule. */
72
+ export function findSequenceBlock(markdown) {
73
+ return findSequenceBlocks(markdown)[0] ?? null;
74
+ }
75
+ function longestBacktickRun(text) {
76
+ let longest = 0;
77
+ let current = 0;
78
+ for (const char of text) {
79
+ if (char === '`') {
80
+ current++;
81
+ if (current > longest)
82
+ longest = current;
83
+ }
84
+ else
85
+ current = 0;
86
+ }
87
+ return longest;
88
+ }
89
+ /**
90
+ * Emit a sequence as a fence wide enough to contain it. A step can be an
91
+ * `execution` call carrying a JS template literal, so three backticks is not
92
+ * always enough; GitHub renders a wider fence identically.
93
+ */
94
+ export function emitSequenceBlock(sequence) {
95
+ const json = JSON.stringify(sequence, null, 2);
96
+ const fence = '`'.repeat(Math.max(3, longestBacktickRun(json) + 1));
97
+ return `${fence}${SEQUENCE_FENCE_INFO}\n${json}\n${fence}`;
98
+ }
99
+ /**
100
+ * Remove sequence blocks and collapse the gap they leave.
101
+ *
102
+ * Used before hashing and before writing a pulled body: the block is a
103
+ * projection of the sequence file, not body content, so leaving it in would
104
+ * make every publish look like a local edit.
105
+ */
106
+ export function stripSequenceBlocks(markdown) {
107
+ const blocks = findSequenceBlocks(markdown);
108
+ if (blocks.length === 0)
109
+ return markdown;
110
+ const lines = markdown.split(/\r?\n/);
111
+ const drop = new Set();
112
+ for (const block of blocks) {
113
+ const bodyLines = block.content === '' ? 0 : block.content.split('\n').length;
114
+ // opening fence + body + closing fence
115
+ for (let i = block.startLine; i <= block.startLine + bodyLines + 1; i++)
116
+ drop.add(i);
117
+ }
118
+ return lines
119
+ .filter((_, i) => !drop.has(i))
120
+ .join('\n')
121
+ .replace(/\n{3,}/g, '\n\n')
122
+ .trim();
123
+ }
124
+ // =============================================================================
125
+ // Remote sequences
126
+ // =============================================================================
127
+ const stepSchema = z.object({
128
+ tool: z.string().min(1),
129
+ params: z.record(z.any()).optional(),
130
+ delay: z.number().optional(),
131
+ comment: z.string().optional(),
132
+ }).strip();
133
+ /** Deliberately a whitelist with .strip(): unknown keys from a public issue
134
+ * are dropped rather than carried onto disk. `id` is regenerated locally. */
135
+ const remoteSequenceSchema = z.object({
136
+ name: z.string().optional(),
137
+ description: z.string().optional(),
138
+ expectedOutcome: z.string().optional(),
139
+ startUrl: z.string().optional(),
140
+ commands: z.array(stepSchema).min(1).max(MAX_STEPS),
141
+ teardown: z.array(stepSchema).max(MAX_STEPS).optional(),
142
+ tags: z.array(z.string()).optional(),
143
+ requiredSockets: z.array(z.string()).optional(),
144
+ requiredConnections: z.array(z.object({
145
+ reference: z.string(),
146
+ url: z.string().optional(),
147
+ profile: z.string().optional(),
148
+ forceNewInstance: z.boolean().optional(),
149
+ role: z.string().optional(),
150
+ }).strip()).optional(),
151
+ }).strip();
152
+ export function parseRemoteSequence(json) {
153
+ if (json.length > MAX_SEQUENCE_BYTES) {
154
+ return { ok: false, reason: `Sequence is ${Math.round(json.length / 1024)}KB, over the 256KB limit` };
155
+ }
156
+ let raw;
157
+ try {
158
+ raw = JSON.parse(json);
159
+ }
160
+ catch (err) {
161
+ return { ok: false, reason: `Not valid JSON: ${err.message}` };
162
+ }
163
+ const parsed = remoteSequenceSchema.safeParse(raw);
164
+ if (!parsed.success) {
165
+ const issue = parsed.error.issues[0];
166
+ const path = issue.path.length > 0 ? issue.path.join('.') : '(root)';
167
+ return { ok: false, reason: `${path}: ${issue.message}` };
168
+ }
169
+ return { ok: true, sequence: parsed.data };
170
+ }
171
+ /** What this sequence would do, in the terms a reader needs before running it. */
172
+ export function auditSequence(sequence) {
173
+ const all = [...sequence.commands, ...(sequence.teardown ?? [])];
174
+ const tools = [...new Set(all.map(step => step.tool))].sort();
175
+ return {
176
+ steps: all.length,
177
+ tools,
178
+ privileged: tools.filter(tool => PRIVILEGED_TOOLS.has(tool)),
179
+ };
180
+ }
181
+ //# sourceMappingURL=issue-body.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"issue-body.js","sourceRoot":"","sources":["../../src/github/issue-body.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=issue-body.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"issue-body.test.d.ts","sourceRoot":"","sources":["../../src/github/issue-body.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,157 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { extractFencedBlocks, findSequenceBlock, findSequenceBlocks, emitSequenceBlock, stripSequenceBlocks, parseRemoteSequence, auditSequence, } from './issue-body.js';
3
+ const SEQ = { name: 'repro', commands: [{ tool: 'navigate', params: { url: 'http://x' } }] };
4
+ describe('fence scanning', () => {
5
+ it('round-trips a sequence through emit and find', () => {
6
+ const body = `## Steps\n\n${emitSequenceBlock(SEQ)}\n`;
7
+ const block = findSequenceBlock(body);
8
+ expect(block).not.toBeNull();
9
+ expect(JSON.parse(block.content)).toEqual(SEQ);
10
+ });
11
+ it('ignores a plain code block that precedes the sequence (the bug-001 shape)', () => {
12
+ const body = [
13
+ '## Steps to reproduce',
14
+ '',
15
+ '```js',
16
+ '// suspect this handler is missing a guard',
17
+ 'submitButton.addEventListener("click", handleSubmit);',
18
+ '```',
19
+ '',
20
+ emitSequenceBlock(SEQ),
21
+ '',
22
+ ].join('\n');
23
+ const blocks = extractFencedBlocks(body);
24
+ expect(blocks).toHaveLength(2);
25
+ expect(blocks[0].info).toBe('js');
26
+ expect(JSON.parse(findSequenceBlock(body).content)).toEqual(SEQ);
27
+ });
28
+ it('widens the fence when the sequence itself contains backticks', () => {
29
+ const withTemplate = {
30
+ name: 'repro',
31
+ commands: [{ tool: 'execution', params: { expression: 'const s = `a ``` b`; return s;' } }],
32
+ };
33
+ const emitted = emitSequenceBlock(withTemplate);
34
+ // A 3-backtick fence would be closed early by the payload.
35
+ expect(emitted.startsWith('````')).toBe(true);
36
+ expect(JSON.parse(findSequenceBlock(emitted).content)).toEqual(withTemplate);
37
+ });
38
+ it('does not let a shorter inner fence close a longer outer one', () => {
39
+ const body = ['````text', 'here is how you write one:', '```json devharness-sequence', '```', '````'].join('\n');
40
+ const blocks = extractFencedBlocks(body);
41
+ expect(blocks).toHaveLength(1);
42
+ expect(blocks[0].info).toBe('text');
43
+ // The inner block is documentation, not a sequence to run.
44
+ expect(findSequenceBlock(body)).toBeNull();
45
+ });
46
+ it('takes the first sequence block, not the last', () => {
47
+ const first = { name: 'first', commands: [{ tool: 'navigate' }] };
48
+ const second = { name: 'second', commands: [{ tool: 'navigate' }] };
49
+ const body = `${emitSequenceBlock(first)}\n\ntext\n\n${emitSequenceBlock(second)}`;
50
+ expect(findSequenceBlocks(body)).toHaveLength(2);
51
+ expect(JSON.parse(findSequenceBlock(body).content).name).toBe('first');
52
+ });
53
+ it('only matches the devharness tag, not any json block', () => {
54
+ const body = '```json\n{"commands":[]}\n```';
55
+ expect(findSequenceBlock(body)).toBeNull();
56
+ expect(extractFencedBlocks(body)[0].info).toBe('json');
57
+ });
58
+ it('handles CRLF input', () => {
59
+ const body = `## Steps\r\n\r\n${emitSequenceBlock(SEQ).replace(/\n/g, '\r\n')}\r\n`;
60
+ expect(JSON.parse(findSequenceBlock(body).content)).toEqual(SEQ);
61
+ });
62
+ it('treats an unterminated fence as a block rather than dropping it', () => {
63
+ const body = '```json devharness-sequence\n{"commands":[]}';
64
+ expect(findSequenceBlock(body)).not.toBeNull();
65
+ });
66
+ });
67
+ describe('stripSequenceBlocks', () => {
68
+ it('removes the block and leaves the prose', () => {
69
+ const body = `## Steps\n\nDo the thing.\n\n${emitSequenceBlock(SEQ)}\n`;
70
+ expect(stripSequenceBlocks(body)).toBe('## Steps\n\nDo the thing.');
71
+ });
72
+ it('leaves other code blocks alone', () => {
73
+ const body = ['Prose.', '', '```js', 'code();', '```', '', emitSequenceBlock(SEQ)].join('\n');
74
+ const stripped = stripSequenceBlocks(body);
75
+ expect(stripped).toContain('```js');
76
+ expect(stripped).not.toContain('devharness-sequence');
77
+ });
78
+ it('is stable across strip -> re-emit -> strip', () => {
79
+ const original = '## Steps\n\nDo the thing.';
80
+ const published = `${original}\n\n${emitSequenceBlock(SEQ)}`;
81
+ const once = stripSequenceBlocks(published);
82
+ const twice = stripSequenceBlocks(`${once}\n\n${emitSequenceBlock(SEQ)}`);
83
+ expect(once).toBe(original);
84
+ expect(twice).toBe(original);
85
+ });
86
+ it('returns the input untouched when there is no sequence', () => {
87
+ const body = 'Just prose.\n\n```js\ncode();\n```';
88
+ expect(stripSequenceBlocks(body)).toBe(body);
89
+ });
90
+ });
91
+ describe('parseRemoteSequence', () => {
92
+ it('accepts a well-formed sequence', () => {
93
+ const result = parseRemoteSequence(JSON.stringify(SEQ));
94
+ expect(result.ok).toBe(true);
95
+ });
96
+ it('drops unknown top-level keys rather than carrying them to disk', () => {
97
+ const result = parseRemoteSequence(JSON.stringify({
98
+ ...SEQ,
99
+ id: 'seq-attacker',
100
+ somethingElse: { nested: true },
101
+ }));
102
+ expect(result.ok).toBe(true);
103
+ if (!result.ok)
104
+ return;
105
+ expect(result.sequence).not.toHaveProperty('id');
106
+ expect(result.sequence).not.toHaveProperty('somethingElse');
107
+ });
108
+ it('rejects a payload over the size limit before parsing it', () => {
109
+ const huge = JSON.stringify({ commands: [{ tool: 'navigate', params: { pad: 'x'.repeat(300_000) } }] });
110
+ const result = parseRemoteSequence(huge);
111
+ expect(result).toMatchObject({ ok: false });
112
+ if (result.ok)
113
+ return;
114
+ expect(result.reason).toContain('256KB');
115
+ });
116
+ it('rejects malformed JSON, missing commands, a non-string tool, and too many steps', () => {
117
+ expect(parseRemoteSequence('{oops').ok).toBe(false);
118
+ expect(parseRemoteSequence('{"name":"x"}').ok).toBe(false);
119
+ expect(parseRemoteSequence('{"commands":[{"tool":42}]}').ok).toBe(false);
120
+ expect(parseRemoteSequence('{"commands":[]}').ok).toBe(false);
121
+ const tooMany = { commands: Array.from({ length: 501 }, () => ({ tool: 'navigate' })) };
122
+ expect(parseRemoteSequence(JSON.stringify(tooMany)).ok).toBe(false);
123
+ });
124
+ it('names the offending path in the reason', () => {
125
+ const result = parseRemoteSequence('{"commands":[{"tool":""}]}');
126
+ expect(result).toMatchObject({ ok: false });
127
+ if (result.ok)
128
+ return;
129
+ expect(result.reason).toContain('commands.0.tool');
130
+ });
131
+ });
132
+ describe('auditSequence', () => {
133
+ it('lists tools and flags the privileged ones', () => {
134
+ const parsed = parseRemoteSequence(JSON.stringify({
135
+ commands: [
136
+ { tool: 'navigate' },
137
+ { tool: 'input' },
138
+ { tool: 'execution', params: { expression: 'fetch("/x")' } },
139
+ ],
140
+ teardown: [{ tool: 'server', params: { action: 'stop' } }],
141
+ }));
142
+ expect(parsed.ok).toBe(true);
143
+ if (!parsed.ok)
144
+ return;
145
+ const audit = auditSequence(parsed.sequence);
146
+ expect(audit.steps).toBe(4);
147
+ expect(audit.tools).toEqual(['execution', 'input', 'navigate', 'server']);
148
+ expect(audit.privileged).toEqual(['execution', 'server']);
149
+ });
150
+ it('reports nothing privileged for a plain UI sequence', () => {
151
+ const parsed = parseRemoteSequence(JSON.stringify(SEQ));
152
+ if (!parsed.ok)
153
+ throw new Error('expected ok');
154
+ expect(auditSequence(parsed.sequence).privileged).toEqual([]);
155
+ });
156
+ });
157
+ //# sourceMappingURL=issue-body.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"issue-body.test.js","sourceRoot":"","sources":["../../src/github/issue-body.test.ts"],"names":[],"mappings":"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"}
@@ -60,6 +60,13 @@ export declare function setWorkingDirOverride(dir: string): void;
60
60
  * @returns Full path like /project/.devharness/logs/debug.log
61
61
  */
62
62
  export declare function getOutputPath(...args: [...string[]] | [...string[], z.infer<typeof pathOptionsSchema>]): string;
63
+ /**
64
+ * The project directory holding .devharness - the cwd for subprocesses that
65
+ * resolve their own context from it (`gh` reading the git remote). Falls back
66
+ * to the home dir when there is no working-dir storage, where `gh` then fails
67
+ * with a clear "no repository" rather than acting on the wrong one.
68
+ */
69
+ export declare function getProjectDir(): string;
63
70
  /**
64
71
  * Get config path with backwards compatibility.
65
72
  * Checks cwd first (if valid and config exists there), then global.
@@ -1 +1 @@
1
- {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/helpers/paths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAQxB;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAyBtD;AAED,kDAAkD;AAClD,wBAAgB,aAAa,IAAI,MAAM,CAItC;AAED,QAAA,MAAM,iBAAiB;;;;;;EAEZ,CAAC;AAEZ,UAAU,UAAU;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAsBD;;;GAGG;AACH,wBAAgB,eAAe,IAAI,UAAU,CAW5C;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAMvD;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,GAAG,IAAI,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,GAAG,CAAC,GAAG,MAAM,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC,GACxE,MAAM,CA0BR;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAatC;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,MAAM,CAUxE;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,GAAG,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,CAGzD;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,OAAO,CAG9C;AAGD,sCAAsC;AACtC,wBAAgB,iBAAiB,CAAC,GAAG,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,CAE/D;AAED,mEAAmE;AACnE,wBAAgB,aAAa,CAAC,GAAG,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,CAE3D;AAED,sCAAsC;AACtC,eAAO,MAAM,iBAAiB,sBAAgB,CAAC"}
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/helpers/paths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAQxB;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAyBtD;AAED,kDAAkD;AAClD,wBAAgB,aAAa,IAAI,MAAM,CAItC;AAED,QAAA,MAAM,iBAAiB;;;;;;EAEZ,CAAC;AAEZ,UAAU,UAAU;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAsBD;;;GAGG;AACH,wBAAgB,eAAe,IAAI,UAAU,CAW5C;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAMvD;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,GAAG,IAAI,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,GAAG,CAAC,GAAG,MAAM,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC,GACxE,MAAM,CA0BR;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAatC;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,MAAM,CAUxE;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,GAAG,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,CAGzD;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,OAAO,CAG9C;AAGD,sCAAsC;AACtC,wBAAgB,iBAAiB,CAAC,GAAG,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,CAE/D;AAED,mEAAmE;AACnE,wBAAgB,aAAa,CAAC,GAAG,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,CAE3D;AAED,sCAAsC;AACtC,eAAO,MAAM,iBAAiB,sBAAgB,CAAC"}
@@ -10,7 +10,7 @@
10
10
  * recorded sequences, issues. So this migrates rather than switches - see
11
11
  * resolveStateDir.
12
12
  */
13
- import { join } from 'path';
13
+ import { join, dirname } from 'path';
14
14
  import { homedir, tmpdir } from 'os';
15
15
  import { existsSync, accessSync, constants, renameSync } from 'fs';
16
16
  import { z } from 'zod';
@@ -149,6 +149,15 @@ export function getOutputPath(...args) {
149
149
  }
150
150
  return join(base, ...segments);
151
151
  }
152
+ /**
153
+ * The project directory holding .devharness - the cwd for subprocesses that
154
+ * resolve their own context from it (`gh` reading the git remote). Falls back
155
+ * to the home dir when there is no working-dir storage, where `gh` then fails
156
+ * with a clear "no repository" rather than acting on the wrong one.
157
+ */
158
+ export function getProjectDir() {
159
+ return dirname(getOutputPath());
160
+ }
152
161
  /**
153
162
  * Get config path with backwards compatibility.
154
163
  * Checks cwd first (if valid and config exists there), then global.
@@ -1 +1 @@
1
- {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/helpers/paths.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/helpers/paths.ts"],"names":[],"mappings":"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"}
package/build/index.js CHANGED
@@ -1327,7 +1327,10 @@ const allTools = {
1327
1327
  if (!resolved?.puppeteerManager)
1328
1328
  return null;
1329
1329
  return resolved.puppeteerManager.getPage();
1330
- }) : {}),
1330
+ },
1331
+ // Lazy: allTools is defined below. Lets a pulled sequence be checked
1332
+ // against the live tool list before it is written to disk.
1333
+ () => Object.keys(allTools)) : {}),
1331
1334
  // Dashboard tools (lazy-initialized in main())
1332
1335
  ...(configManager.isToolEnabled('dashboard') ? createDashboardTools() : {}),
1333
1336
  };
@@ -1450,6 +1453,9 @@ Edit ${configPath} to resolve, then restart the MCP server.`,
1450
1453
  // Pass validated data to handler
1451
1454
  try {
1452
1455
  const result = await tool.handler(validation.data, extra?.signal);
1456
+ if (commandIndex !== null) {
1457
+ commandRecorder.attachResult(commandIndex, result);
1458
+ }
1453
1459
  // Prepend port failure prefix if any
1454
1460
  if (portCheck.prefix) {
1455
1461
  prependToResponse(result, portCheck.prefix);