@promptbook/cli 0.114.0-10 → 0.114.0-12

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 (75) hide show
  1. package/apps/agents-server/src/app/admin/messages/MessagesClient.tsx +8 -1
  2. package/apps/agents-server/src/app/admin/messages/send-email/SendEmailClient.tsx +193 -63
  3. package/apps/agents-server/src/app/admin/messages/send-email/actions.ts +25 -28
  4. package/apps/agents-server/src/app/admin/messages/send-email/emailTesting.ts +153 -0
  5. package/apps/agents-server/src/app/admin/messages/send-email/emailTestingAccess.ts +30 -0
  6. package/apps/agents-server/src/app/admin/messages/send-email/page.tsx +18 -5
  7. package/apps/agents-server/src/app/api/internal/agent-goal-chat-planned-messages/route.ts +34 -7
  8. package/apps/agents-server/src/message-providers/email/stalwart/StalwartMessageProvider.ts +2 -1
  9. package/apps/agents-server/src/utils/email/agentEmailAddress.ts +14 -0
  10. package/apps/agents-server/src/utils/email/sendEmailThroughStalwart.ts +2 -1
  11. package/esm/index.es.js +404 -115
  12. package/esm/index.es.js.map +1 -1
  13. package/esm/scripts/run-codex-prompts/common/runGoScript/$spawnLoggedBashScript.d.ts +25 -0
  14. package/esm/scripts/run-codex-prompts/common/runGoScript/$terminateLoggedBashProcessTree.d.ts +11 -0
  15. package/esm/scripts/run-codex-prompts/common/runGoScript/scriptExecutionLog.d.ts +4 -0
  16. package/esm/src/book-2.0/agent-source/AgentSourceParseResult.d.ts +5 -0
  17. package/esm/src/book-2.0/agent-source/ParsedUnknownCommitment.d.ts +26 -0
  18. package/esm/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments.unknown.test.d.ts +1 -0
  19. package/esm/src/book-2.0/agent-source/getBookCommitmentLineType.d.ts +19 -0
  20. package/esm/src/book-2.0/agent-source/parseAgentSourceWithCommitments.unknown.test.d.ts +1 -0
  21. package/esm/src/book-components/BookEditor/createUnknownCommitmentDiagnostics.d.ts +28 -0
  22. package/esm/src/book-components/BookEditor/createUnknownCommitmentDiagnostics.test.d.ts +1 -0
  23. package/esm/src/cli/cli-commands/common/harness/$checkHarnessInstallation.d.ts +3 -3
  24. package/esm/src/cli/cli-commands/common/harness/$checkHarnessInstallation.test.d.ts +1 -0
  25. package/esm/src/cli/cli-commands/common/harness/$ensureHarnessInstallations.d.ts +3 -3
  26. package/esm/src/cli/cli-commands/common/harness/HarnessInstallationStatus.d.ts +3 -2
  27. package/esm/src/cli/cli-commands/common/harnessUpdateCliOptions.d.ts +29 -0
  28. package/esm/src/cli/cli-commands/common/harnessUpdateCliOptions.test.d.ts +1 -0
  29. package/esm/src/executables/platforms/locateAppOnWindows.d.ts +1 -1
  30. package/esm/src/executables/platforms/locateAppOnWindows.test.d.ts +1 -0
  31. package/esm/src/version.d.ts +1 -1
  32. package/package.json +1 -1
  33. package/src/book-2.0/agent-source/AgentSourceParseResult.ts +6 -0
  34. package/src/book-2.0/agent-source/ParsedUnknownCommitment.ts +29 -0
  35. package/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments/augmentAgentModelRequirementsFromSource.ts +38 -1
  36. package/src/book-2.0/agent-source/getBookCommitmentLineType.ts +36 -0
  37. package/src/book-2.0/agent-source/parseAgentSourceWithCommitments.ts +128 -61
  38. package/src/book-components/BookEditor/BookEditorMonaco.tsx +6 -1
  39. package/src/book-components/BookEditor/BookEditorMonacoTokenization.ts +3 -1
  40. package/src/book-components/BookEditor/createUnknownCommitmentDiagnostics.ts +50 -0
  41. package/src/cli/cli-commands/coder/boilerplateTemplates.ts +3 -15
  42. package/src/cli/cli-commands/coder/init.ts +11 -2
  43. package/src/cli/cli-commands/coder/ping.ts +11 -1
  44. package/src/cli/cli-commands/coder/run.ts +11 -2
  45. package/src/cli/cli-commands/coder/server.ts +11 -2
  46. package/src/cli/cli-commands/common/harness/$applyHarnessInstallationStatus.ts +5 -0
  47. package/src/cli/cli-commands/common/harness/$checkHarnessInstallation.ts +17 -5
  48. package/src/cli/cli-commands/common/harness/$ensureHarnessInstallations.ts +6 -3
  49. package/src/cli/cli-commands/common/harness/HarnessInstallationStatus.ts +3 -2
  50. package/src/cli/cli-commands/common/harnessUpdateCliOptions.ts +49 -0
  51. package/src/executables/platforms/locateAppOnWindows.ts +12 -9
  52. package/src/other/templates/getTemplatesPipelineCollection.ts +881 -685
  53. package/src/version.ts +2 -2
  54. package/src/versions.txt +1 -0
  55. package/umd/index.umd.js +403 -114
  56. package/umd/index.umd.js.map +1 -1
  57. package/umd/scripts/run-codex-prompts/common/runGoScript/$spawnLoggedBashScript.d.ts +25 -0
  58. package/umd/scripts/run-codex-prompts/common/runGoScript/$terminateLoggedBashProcessTree.d.ts +11 -0
  59. package/umd/scripts/run-codex-prompts/common/runGoScript/scriptExecutionLog.d.ts +4 -0
  60. package/umd/src/book-2.0/agent-source/AgentSourceParseResult.d.ts +5 -0
  61. package/umd/src/book-2.0/agent-source/ParsedUnknownCommitment.d.ts +26 -0
  62. package/umd/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments.unknown.test.d.ts +1 -0
  63. package/umd/src/book-2.0/agent-source/getBookCommitmentLineType.d.ts +19 -0
  64. package/umd/src/book-2.0/agent-source/parseAgentSourceWithCommitments.unknown.test.d.ts +1 -0
  65. package/umd/src/book-components/BookEditor/createUnknownCommitmentDiagnostics.d.ts +28 -0
  66. package/umd/src/book-components/BookEditor/createUnknownCommitmentDiagnostics.test.d.ts +1 -0
  67. package/umd/src/cli/cli-commands/common/harness/$checkHarnessInstallation.d.ts +3 -3
  68. package/umd/src/cli/cli-commands/common/harness/$checkHarnessInstallation.test.d.ts +1 -0
  69. package/umd/src/cli/cli-commands/common/harness/$ensureHarnessInstallations.d.ts +3 -3
  70. package/umd/src/cli/cli-commands/common/harness/HarnessInstallationStatus.d.ts +3 -2
  71. package/umd/src/cli/cli-commands/common/harnessUpdateCliOptions.d.ts +29 -0
  72. package/umd/src/cli/cli-commands/common/harnessUpdateCliOptions.test.d.ts +1 -0
  73. package/umd/src/executables/platforms/locateAppOnWindows.d.ts +1 -1
  74. package/umd/src/executables/platforms/locateAppOnWindows.test.d.ts +1 -0
  75. package/umd/src/version.d.ts +1 -1
@@ -3,7 +3,9 @@ import type { BookCommitment } from '../../commitments/_base/BookCommitment';
3
3
  import type { ParsedCommitment } from '../../commitments/_base/ParsedCommitment';
4
4
  import { COMMITMENT_REGISTRY } from '../../commitments/index';
5
5
  import type { AgentSourceParseResult } from './AgentSourceParseResult';
6
+ import { getBookCommitmentLineType } from './getBookCommitmentLineType';
6
7
  import { parseAgentSourcePrelude } from './parseAgentSourcePrelude';
8
+ import type { ParsedUnknownCommitment } from './ParsedUnknownCommitment';
7
9
  import type { string_book } from './string_book';
8
10
 
9
11
  /**
@@ -12,6 +14,39 @@ import type { string_book } from './string_book';
12
14
  */
13
15
  const HORIZONTAL_LINE_PATTERN = /^[\s]*[-_*][\s]*[-_*][\s]*[-_*][\s]*[-_*]*[\s]*$/;
14
16
 
17
+ /**
18
+ * Registered commitment block currently being collected from Book source.
19
+ *
20
+ * @private internal type of `parseAgentSourceWithCommitments`
21
+ */
22
+ type CurrentKnownCommitment = {
23
+ readonly kind: 'known';
24
+ readonly type: string;
25
+ readonly startLineNumber: number;
26
+ readonly originalStartLine: string;
27
+ readonly contentLines: string[];
28
+ };
29
+
30
+ /**
31
+ * Unknown commitment block currently being collected from Book source.
32
+ *
33
+ * @private internal type of `parseAgentSourceWithCommitments`
34
+ */
35
+ type CurrentUnknownCommitment = {
36
+ readonly kind: 'unknown';
37
+ readonly type: string;
38
+ readonly startLineNumber: number;
39
+ readonly originalStartLine: string;
40
+ readonly sourceLines: string[];
41
+ };
42
+
43
+ /**
44
+ * Commitment-like source block currently being collected.
45
+ *
46
+ * @private internal type of `parseAgentSourceWithCommitments`
47
+ */
48
+ type CurrentCommitment = CurrentKnownCommitment | CurrentUnknownCommitment;
49
+
15
50
  /**
16
51
  * Parses agent source using the new commitment system with multiline support
17
52
  * This function replaces the hardcoded commitment parsing in the original parseAgentSource
@@ -26,6 +61,7 @@ export function parseAgentSourceWithCommitments(agentSource: string_book): Omit<
26
61
  return {
27
62
  agentName: null,
28
63
  commitments: [],
64
+ unknownCommitments: [],
29
65
  nonCommitmentLines: [],
30
66
  };
31
67
  }
@@ -33,15 +69,11 @@ export function parseAgentSourceWithCommitments(agentSource: string_book): Omit<
33
69
  const { lines, agentName, agentNameLineIndex, agentNameLineNumber } = parseAgentSourcePrelude(agentSource);
34
70
 
35
71
  const commitments: ParsedCommitment[] = [];
72
+ const unknownCommitments: ParsedUnknownCommitment[] = [];
36
73
  const nonCommitmentLines: string[] = agentNameLineIndex >= 0 ? [lines[agentNameLineIndex]!] : [];
37
74
 
38
- // Parse commitments with multiline support
39
- let currentCommitment: {
40
- type: string;
41
- startLineNumber: number;
42
- originalStartLine: string;
43
- contentLines: string[];
44
- } | null = null;
75
+ // Parse commitments with multiline support.
76
+ let currentCommitment: CurrentCommitment | null = null;
45
77
 
46
78
  // Process lines starting from after the agent name line
47
79
  const startIndex = agentNameLineIndex >= 0 ? agentNameLineIndex + 1 : 0;
@@ -58,24 +90,12 @@ export function parseAgentSourceWithCommitments(agentSource: string_book): Omit<
58
90
  if (trimmedLine.startsWith('```')) {
59
91
  isInsideCodeBlock = !isInsideCodeBlock;
60
92
 
61
- if (currentCommitment) {
62
- // If we are inside a commitment, the code block is part of it
63
- currentCommitment.contentLines.push(line);
64
- } else {
65
- // If we are not inside a commitment, the code block is non-commitment
66
- nonCommitmentLines.push(line);
67
- }
93
+ appendLineToCurrentCommitmentOrNonCommitmentLines(currentCommitment, line, nonCommitmentLines);
68
94
  continue;
69
95
  }
70
96
 
71
97
  if (isInsideCodeBlock) {
72
- if (currentCommitment) {
73
- // If we are inside a commitment and a code block, the line is part of the commitment
74
- currentCommitment.contentLines.push(line);
75
- } else {
76
- // If we are inside a code block but not a commitment, the line is non-commitment
77
- nonCommitmentLines.push(line);
78
- }
98
+ appendLineToCurrentCommitmentOrNonCommitmentLines(currentCommitment, line, nonCommitmentLines);
79
99
  continue;
80
100
  }
81
101
 
@@ -86,16 +106,7 @@ export function parseAgentSourceWithCommitments(agentSource: string_book): Omit<
86
106
  const match = typeRegex.exec(line.trim());
87
107
 
88
108
  if (match && match.groups?.type) {
89
- // Save the previous commitment if it exists
90
- if (currentCommitment) {
91
- const fullContent = currentCommitment.contentLines.join('\n');
92
- commitments.push({
93
- type: currentCommitment.type as BookCommitment,
94
- content: spaceTrim(fullContent),
95
- originalLine: currentCommitment.originalStartLine,
96
- lineNumber: currentCommitment.startLineNumber,
97
- });
98
- }
109
+ appendCurrentCommitment(currentCommitment, commitments, unknownCommitments);
99
110
 
100
111
  // Extract the initial content from the commitment line
101
112
  const fullRegex = definition.createRegex();
@@ -104,6 +115,7 @@ export function parseAgentSourceWithCommitments(agentSource: string_book): Omit<
104
115
 
105
116
  // Start a new commitment
106
117
  currentCommitment = {
118
+ kind: 'known',
107
119
  type: definition.type,
108
120
  startLineNumber: i + 1,
109
121
  originalStartLine: line,
@@ -114,51 +126,106 @@ export function parseAgentSourceWithCommitments(agentSource: string_book): Omit<
114
126
  }
115
127
  }
116
128
 
129
+ if (foundNewCommitment) {
130
+ continue;
131
+ }
132
+
133
+ const unknownCommitmentType = getBookCommitmentLineType(line);
134
+ if (unknownCommitmentType) {
135
+ appendCurrentCommitment(currentCommitment, commitments, unknownCommitments);
136
+ currentCommitment = {
137
+ kind: 'unknown',
138
+ type: unknownCommitmentType,
139
+ startLineNumber: i + 1,
140
+ originalStartLine: line,
141
+ sourceLines: [line],
142
+ };
143
+ continue;
144
+ }
145
+
117
146
  // Check if this is a horizontal line (ends any current commitment)
118
147
  const isHorizontalLine = HORIZONTAL_LINE_PATTERN.test(line);
119
148
  if (isHorizontalLine) {
120
- // Save the current commitment if it exists
121
- if (currentCommitment) {
122
- const fullContent = currentCommitment.contentLines.join('\n');
123
- commitments.push({
124
- type: currentCommitment.type as BookCommitment,
125
- content: spaceTrim(fullContent),
126
- originalLine: currentCommitment.originalStartLine,
127
- lineNumber: currentCommitment.startLineNumber,
128
- });
129
- currentCommitment = null;
130
- }
149
+ appendCurrentCommitment(currentCommitment, commitments, unknownCommitments);
150
+ currentCommitment = null;
131
151
  // Add horizontal line to non-commitment lines
132
152
  nonCommitmentLines.push(line);
133
153
  continue;
134
154
  }
135
155
 
136
- if (!foundNewCommitment) {
137
- if (currentCommitment) {
138
- // This line belongs to the current commitment
139
- currentCommitment.contentLines.push(line);
140
- } else {
141
- // This line is not part of any commitment
142
- nonCommitmentLines.push(line);
143
- }
144
- }
156
+ appendLineToCurrentCommitmentOrNonCommitmentLines(currentCommitment, line, nonCommitmentLines);
145
157
  }
146
158
 
147
- // Don't forget to save the last commitment if it exists
148
- if (currentCommitment) {
149
- const fullContent = currentCommitment.contentLines.join('\n');
150
- commitments.push({
151
- type: currentCommitment.type as BookCommitment,
152
- content: spaceTrim(fullContent) as BookCommitment,
153
- originalLine: currentCommitment.originalStartLine,
154
- lineNumber: currentCommitment.startLineNumber,
155
- });
156
- }
159
+ appendCurrentCommitment(currentCommitment, commitments, unknownCommitments);
157
160
 
158
161
  return {
159
162
  agentName,
160
163
  agentNameLineNumber,
161
164
  commitments,
165
+ unknownCommitments,
162
166
  nonCommitmentLines,
163
167
  };
164
168
  }
169
+
170
+ /**
171
+ * Stores the current known or unknown commitment in its appropriate parsed collection.
172
+ *
173
+ * @param currentCommitment - Commitment-like block collected so far.
174
+ * @param commitments - Parsed registered commitments to append to.
175
+ * @param unknownCommitments - Parsed unregistered commitments to append to.
176
+ *
177
+ * @private internal utility of `parseAgentSourceWithCommitments`
178
+ */
179
+ function appendCurrentCommitment(
180
+ currentCommitment: CurrentCommitment | null,
181
+ commitments: ParsedCommitment[],
182
+ unknownCommitments: ParsedUnknownCommitment[],
183
+ ): void {
184
+ if (!currentCommitment) {
185
+ return;
186
+ }
187
+
188
+ if (currentCommitment.kind === 'unknown') {
189
+ unknownCommitments.push({
190
+ type: currentCommitment.type,
191
+ originalLine: currentCommitment.originalStartLine,
192
+ lineNumber: currentCommitment.startLineNumber,
193
+ source: spaceTrim(currentCommitment.sourceLines.join('\n')),
194
+ });
195
+ return;
196
+ }
197
+
198
+ commitments.push({
199
+ type: currentCommitment.type as BookCommitment,
200
+ content: spaceTrim(currentCommitment.contentLines.join('\n')),
201
+ originalLine: currentCommitment.originalStartLine,
202
+ lineNumber: currentCommitment.startLineNumber,
203
+ });
204
+ }
205
+
206
+ /**
207
+ * Adds one source line to the active commitment-like block, or to plain source content when none is active.
208
+ *
209
+ * @param currentCommitment - Commitment-like block currently being collected.
210
+ * @param line - Source line to append.
211
+ * @param nonCommitmentLines - Plain source lines to append to when no block is active.
212
+ *
213
+ * @private internal utility of `parseAgentSourceWithCommitments`
214
+ */
215
+ function appendLineToCurrentCommitmentOrNonCommitmentLines(
216
+ currentCommitment: CurrentCommitment | null,
217
+ line: string,
218
+ nonCommitmentLines: string[],
219
+ ): void {
220
+ if (!currentCommitment) {
221
+ nonCommitmentLines.push(line);
222
+ return;
223
+ }
224
+
225
+ if (currentCommitment.kind === 'unknown') {
226
+ currentCommitment.sourceLines.push(line);
227
+ return;
228
+ }
229
+
230
+ currentCommitment.contentLines.push(line);
231
+ }
@@ -15,6 +15,7 @@ import { BookEditorMonacoConstants } from './BookEditorMonacoConstants';
15
15
  import { BookEditorMonacoUploadPanel } from './BookEditorMonacoUploadPanel';
16
16
  import { BOOK_EDITOR_RENDER_THEME, resolveBookEditorRenderTheme } from './BookEditorTheme';
17
17
  import { createDeprecatedCommitmentDiagnostics } from './createDeprecatedCommitmentDiagnostics.browser';
18
+ import { createUnknownCommitmentDiagnostics } from './createUnknownCommitmentDiagnostics';
18
19
  import { useBookEditorMonacoDecorations } from './useBookEditorMonacoDecorations';
19
20
  import { useBookEditorMonacoDiagnostics } from './useBookEditorMonacoDiagnostics';
20
21
  import { useBookEditorMonacoInteractions } from './useBookEditorMonacoInteractions';
@@ -257,7 +258,11 @@ export function BookEditorMonaco(props: BookEditorProps) {
257
258
  editor,
258
259
  handleFiles,
259
260
  });
260
- const combinedDiagnostics = [...(diagnostics || []), ...createDeprecatedCommitmentDiagnostics(value)];
261
+ const combinedDiagnostics = [
262
+ ...(diagnostics || []),
263
+ ...createDeprecatedCommitmentDiagnostics(value),
264
+ ...createUnknownCommitmentDiagnostics(value),
265
+ ];
261
266
  const isActionBarVisible = isBookEditorMonacoActionbarVisible({
262
267
  hoistedMenuItems,
263
268
  isUploadButtonShown,
@@ -1,3 +1,5 @@
1
+ import { createBookCommitmentLineTypeRegex } from '../../book-2.0/agent-source/getBookCommitmentLineType';
2
+
1
3
  /**
2
4
  * Regex source for absolute URL references inside TEAM/FROM/IMPORT commitments.
3
5
  *
@@ -47,7 +49,7 @@ const AGENT_REFERENCE_COMMITMENT_TYPES = ['FROM', 'IMPORT', 'IMPORTS', 'TEAM'] a
47
49
  *
48
50
  * @private function of BookEditorMonaco
49
51
  */
50
- const DYNAMIC_COMMITMENT_REGEX = /^\s*[A-Z][A-Z0-9]+(?:\s+[A-Z][A-Z0-9]+)*(?=\s|$)/;
52
+ const DYNAMIC_COMMITMENT_REGEX = createBookCommitmentLineTypeRegex();
51
53
 
52
54
  /**
53
55
  * Regex pattern to match horizontal lines.
@@ -0,0 +1,50 @@
1
+ import { parseAgentSourceWithCommitments } from '../../book-2.0/agent-source/parseAgentSourceWithCommitments';
2
+ import type { string_book } from '../../book-2.0/agent-source/string_book';
3
+
4
+ /**
5
+ * Monaco diagnostic shown when a Book source uses an uppercase commitment not registered by Promptbook.
6
+ *
7
+ * @private internal type of `BookEditorMonaco`
8
+ */
9
+ type UnknownCommitmentDiagnostic = {
10
+ readonly startLineNumber: number;
11
+ readonly startColumn: number;
12
+ readonly endLineNumber: number;
13
+ readonly endColumn: number;
14
+ readonly message: string;
15
+ readonly source: string;
16
+ readonly severity: 'error';
17
+ };
18
+
19
+ /**
20
+ * Creates Book editor diagnostics for uppercase commitments not registered by Promptbook.
21
+ *
22
+ * Unknown commitment blocks remain valid extra context in the generated system message,
23
+ * but the editor marks their keywords so authors can spot likely typos immediately.
24
+ *
25
+ * @param agentSource - Current editor content.
26
+ * @returns Error markers for all unknown commitment keywords.
27
+ *
28
+ * @private internal utility of `BookEditorMonaco`
29
+ */
30
+ export function createUnknownCommitmentDiagnostics(
31
+ agentSource?: string_book,
32
+ ): ReadonlyArray<UnknownCommitmentDiagnostic> {
33
+ if (!agentSource?.trim()) {
34
+ return [];
35
+ }
36
+
37
+ return parseAgentSourceWithCommitments(agentSource).unknownCommitments.map((commitment) => {
38
+ const startColumn = commitment.originalLine.indexOf(commitment.type) + 1;
39
+
40
+ return {
41
+ startLineNumber: commitment.lineNumber,
42
+ startColumn,
43
+ endLineNumber: commitment.lineNumber,
44
+ endColumn: startColumn + commitment.type.length,
45
+ message: `Unknown commitment \`${commitment.type}\` is added to the system message as extra context.`,
46
+ source: 'Promptbook',
47
+ severity: 'error',
48
+ };
49
+ });
50
+ }
@@ -124,24 +124,12 @@ const DEFAULT_CODER_PROMPT_TEMPLATE_DEFINITIONS = [
124
124
  - @@@
125
125
  - Keep in mind the DRY _(don't repeat yourself)_ principle.
126
126
  - Do a proper analysis of the current functionality before you start implementing.
127
- - Add the changes into the [changelog](changelog/_current-preversion.md)
127
+ - Add the changes into the [changelog](CHANGELOG.md)
128
+ - Update the [README](README.md) if needed.
129
+ - Update the [AGENTS.md](AGENTS.md) for the next job to be done if it makes sense.
128
130
  `),
129
131
  isDefaultProjectTemplate: true,
130
132
  },
131
- {
132
- id: 'agents-server',
133
- relativeFilePath: join(PROMPTS_TEMPLATES_DIRECTORY_PATH, 'agents-server.md'),
134
- slugPrefix: 'agents-server',
135
- content: spaceTrim(`
136
- - @@@
137
- - Keep in mind the DRY _(don't repeat yourself)_ principle.
138
- - Do a proper analysis of the current functionality before you start implementing.
139
- - You are working with the [Agents Server](apps/agents-server)
140
- - If you need to do the database migration, do it
141
- - Add the changes into the [changelog](changelog/_current-preversion.md)
142
- `),
143
- isDefaultProjectTemplate: false,
144
- },
145
133
  ] as const satisfies ReadonlyArray<CoderPromptTemplateDefinition>;
146
134
 
147
135
  /**
@@ -12,6 +12,11 @@ import {
12
12
  import { handleActionErrors } from '../common/handleActionErrors';
13
13
  import { $ensureHarnessInstallations } from '../common/harness/$ensureHarnessInstallations';
14
14
  import { getHarnessDefinition } from '../common/harness/HarnessDefinition';
15
+ import {
16
+ addHarnessUpdateOption,
17
+ normalizeHarnessUpdateCliOptions,
18
+ type HarnessUpdateCliOptions,
19
+ } from '../common/harnessUpdateCliOptions';
15
20
  import type { PromptRunnerHarnessName } from '../common/promptRunnerCliOptions';
16
21
  import { AGENT_CODING_FILE_PATH } from './agentCodingFile';
17
22
  import { AGENTS_FILE_PATH } from './agentsFile';
@@ -66,7 +71,7 @@ export function $initializeCoderInitCommand(program: Program): $side_effect {
66
71
  - CODING_AGENT_GIT_EMAIL
67
72
  - CODING_AGENT_GIT_SIGNING_KEY
68
73
 
69
- Checks that the coding harnesses are installed globally and up to date:
74
+ Checks that the coding harnesses are installed globally and up to date unless \`--no-harness-update\` is used:
70
75
  ${block(listCheckedHarnessLabels())}
71
76
 
72
77
  ${block(CODER_GIT_SYNC_DESCRIPTION)}
@@ -75,10 +80,14 @@ export function $initializeCoderInitCommand(program: Program): $side_effect {
75
80
  );
76
81
 
77
82
  addCoderGitSyncOptions(command);
83
+ addHarnessUpdateOption(command);
78
84
 
79
85
  command.action(
80
86
  handleActionErrors(async (cliOptions) => {
81
87
  const gitSync = normalizeCoderGitSyncCliOptions(cliOptions as CoderGitSyncCliOptions);
88
+ const { isHarnessUpdateCheckEnabled } = normalizeHarnessUpdateCliOptions(
89
+ cliOptions as HarnessUpdateCliOptions,
90
+ );
82
91
  const projectPath = process.cwd();
83
92
 
84
93
  // Note: Import the git synchronization dynamically to keep the CLI fast for runs without `--commit`
@@ -98,7 +107,7 @@ export function $initializeCoderInitCommand(program: Program): $side_effect {
98
107
  commitMessage: 'Initialize Promptbook Coder',
99
108
  });
100
109
 
101
- await $ensureHarnessInstallations(CODER_INIT_CHECKED_HARNESS_NAMES);
110
+ await $ensureHarnessInstallations(CODER_INIT_CHECKED_HARNESS_NAMES, isHarnessUpdateCheckEnabled);
102
111
  }),
103
112
  );
104
113
  }
@@ -5,6 +5,11 @@ import { spaceTrim } from 'spacetrim';
5
5
  import type { $side_effect } from '../../../utils/organization/$side_effect';
6
6
  import { handleActionErrors } from '../common/handleActionErrors';
7
7
  import { $ensureHarnessInstallations } from '../common/harness/$ensureHarnessInstallations';
8
+ import {
9
+ addHarnessUpdateOption,
10
+ normalizeHarnessUpdateCliOptions,
11
+ type HarnessUpdateCliOptions,
12
+ } from '../common/harnessUpdateCliOptions';
8
13
  import type { PromptRunnerSelectionCliOptions } from '../common/promptRunnerCliOptions';
9
14
  import {
10
15
  addPromptRunnerRuntimeOptions,
@@ -33,11 +38,13 @@ export function $initializeCoderPingCommand(program: Program): $side_effect {
33
38
  - Reports the answer of the harness, the response time and the reported usage
34
39
  - Starts the hourly/weekly quota window before you need it, so it is already refreshing when you do
35
40
  - Leaves the project exactly as it was — nothing is read, written, changed or committed
41
+ - Checks that the selected harness is installed globally and up to date unless --no-harness-update is used
36
42
  - Use --no-ui to stream the raw harness output instead of only the compact result
37
43
  `),
38
44
  );
39
45
 
40
46
  addPromptRunnerSelectionOptions(command);
47
+ addHarnessUpdateOption(command);
41
48
  addPromptRunnerRuntimeOptions(command);
42
49
 
43
50
  command.action(
@@ -46,8 +53,11 @@ export function $initializeCoderPingCommand(program: Program): $side_effect {
46
53
  cliOptions as PromptRunnerSelectionCliOptions,
47
54
  { isAgentRequired: true },
48
55
  );
56
+ const { isHarnessUpdateCheckEnabled } = normalizeHarnessUpdateCliOptions(
57
+ cliOptions as HarnessUpdateCliOptions,
58
+ );
49
59
 
50
- await $ensureHarnessInstallations([runnerOptions.agentName]);
60
+ await $ensureHarnessInstallations([runnerOptions.agentName], isHarnessUpdateCheckEnabled);
51
61
 
52
62
  // Note: Import the ping dynamically to avoid loading heavy dependencies until needed
53
63
  const { pingCoderHarness } = await import('../../../../scripts/run-codex-prompts/ping/pingCoderHarness');
@@ -10,6 +10,11 @@ import { createNonNegativeIntegerOptionParser } from '../common/createNonNegativ
10
10
  import { createPositiveIntegerOptionParser } from '../common/createPositiveIntegerOptionParser';
11
11
  import { handleActionErrors } from '../common/handleActionErrors';
12
12
  import { $ensureHarnessInstallations } from '../common/harness/$ensureHarnessInstallations';
13
+ import {
14
+ addHarnessUpdateOption,
15
+ normalizeHarnessUpdateCliOptions,
16
+ type HarnessUpdateCliOptions,
17
+ } from '../common/harnessUpdateCliOptions';
13
18
  import { $ensurePromptbookCliInstallations } from '../common/promptbook-cli/$ensurePromptbookCliInstallations';
14
19
  import type { PromptRunnerCliOptions } from '../common/promptRunnerCliOptions';
15
20
  import {
@@ -48,7 +53,7 @@ export function $initializeCoderRunCommand(program: Program): $side_effect {
48
53
  - Optional --isolate runs every prompt in its own temporary git worktree and merges it back when verified
49
54
  - Optional --preserve-logs keeps temp prompt/log artifacts after successful rounds
50
55
  - Optional --no-ui keeps plain streaming console output for logging and debugging
51
- - Checks that the selected harness is installed globally and up to date before the first prompt
56
+ - Checks that the selected harness is installed globally and up to date before the first prompt unless --no-harness-update is used
52
57
  - In interactive mode, checks local and global Promptbook CLI installations and offers to update them
53
58
  - Supports GPG signing of commits
54
59
  - Optional pre-coding test run that can stop or repair pre-existing failures
@@ -60,6 +65,7 @@ export function $initializeCoderRunCommand(program: Program): $side_effect {
60
65
 
61
66
  command.option('--dry-run', 'Print unwritten prompts without executing', false);
62
67
  addPromptRunnerSelectionOptions(command);
68
+ addHarnessUpdateOption(command);
63
69
  command.option(
64
70
  '--agent <agent-book-path>',
65
71
  'Path to a .book file whose compiled system message is prepended to each coding prompt',
@@ -198,6 +204,9 @@ export function $initializeCoderRunCommand(program: Program): $side_effect {
198
204
  const runnerOptions = normalizePromptRunnerCliOptions(cliOptions as PromptRunnerCliOptions, {
199
205
  isAgentRequired: !dryRun,
200
206
  });
207
+ const { isHarnessUpdateCheckEnabled } = normalizeHarnessUpdateCliOptions(
208
+ cliOptions as HarnessUpdateCliOptions,
209
+ );
201
210
 
202
211
  // [1] Parse the wait options and --no-auto:
203
212
  // default: run automatically through the queue (no waiting between prompts)
@@ -211,7 +220,7 @@ export function $initializeCoderRunCommand(program: Program): $side_effect {
211
220
  return process.exit(0);
212
221
  }
213
222
 
214
- await $ensureHarnessInstallations([runnerOptions.agentName]);
223
+ await $ensureHarnessInstallations([runnerOptions.agentName], isHarnessUpdateCheckEnabled);
215
224
 
216
225
  const waitAfterPrompt = parseOptionalWaitDuration(waitAfterPromptValue, 0);
217
226
  const waitBetweenPrompts = parseOptionalWaitDuration(waitBetweenPromptsValue, 0);
@@ -12,6 +12,11 @@ import type { $side_effect } from '../../../utils/organization/$side_effect';
12
12
  import { createNonNegativeIntegerOptionParser } from '../common/createNonNegativeIntegerOptionParser';
13
13
  import { handleActionErrors } from '../common/handleActionErrors';
14
14
  import { $ensureHarnessInstallations } from '../common/harness/$ensureHarnessInstallations';
15
+ import {
16
+ addHarnessUpdateOption,
17
+ normalizeHarnessUpdateCliOptions,
18
+ type HarnessUpdateCliOptions,
19
+ } from '../common/harnessUpdateCliOptions';
15
20
  import type { PromptRunnerCliOptions } from '../common/promptRunnerCliOptions';
16
21
  import {
17
22
  addPromptRunnerExecutionOptions,
@@ -48,7 +53,7 @@ export function $initializeCoderServerCommand(program: Program): $side_effect {
48
53
 
49
54
  Features:
50
55
  - Runs the same prompt processing as \`ptbk coder run\`
51
- - Checks that the selected harness is installed globally and up to date on startup
56
+ - Checks that the selected harness is installed globally and up to date on startup unless --no-harness-update is used
52
57
  - Does not exit when all prompts are done; polls for new prompt files instead
53
58
  - Serves a kanban board at http://localhost:<port> for visual progress tracking
54
59
  - Allows editing prompt files directly from the browser (Trello-style)
@@ -64,6 +69,7 @@ export function $initializeCoderServerCommand(program: Program): $side_effect {
64
69
  );
65
70
  command.option('--dry-run', 'Print unwritten prompts without executing', false);
66
71
  addPromptRunnerSelectionOptions(command);
72
+ addHarnessUpdateOption(command);
67
73
  command.option(
68
74
  '--agent <agent-book-path>',
69
75
  'Path to a .book file whose compiled system message is prepended to each coding prompt',
@@ -173,8 +179,11 @@ export function $initializeCoderServerCommand(program: Program): $side_effect {
173
179
  const runnerOptions = normalizePromptRunnerCliOptions(cliOptions as PromptRunnerCliOptions, {
174
180
  isAgentRequired: !dryRun,
175
181
  });
182
+ const { isHarnessUpdateCheckEnabled } = normalizeHarnessUpdateCliOptions(
183
+ cliOptions as HarnessUpdateCliOptions,
184
+ );
176
185
 
177
- await $ensureHarnessInstallations([runnerOptions.agentName]);
186
+ await $ensureHarnessInstallations([runnerOptions.agentName], isHarnessUpdateCheckEnabled);
178
187
 
179
188
  // [1] Parse the wait options and --no-auto (same logic as `coder run`)
180
189
  const waitForUser = !auto;
@@ -20,6 +20,11 @@ export async function $applyHarnessInstallationStatus(status: HarnessInstallatio
20
20
  return;
21
21
  }
22
22
 
23
+ if (installationState === 'installed') {
24
+ console.info(colors.gray(`✔ ${definition.label} ${installedVersion} is installed. Skipped checking for updates.`));
25
+ return;
26
+ }
27
+
23
28
  if (installationState === 'unknown') {
24
29
  console.info(
25
30
  colors.gray(
@@ -5,21 +5,28 @@ import type { HarnessDefinition } from './HarnessDefinition';
5
5
  import type { HarnessInstallationState, HarnessInstallationStatus } from './HarnessInstallationStatus';
6
6
 
7
7
  /**
8
- * Detects whether one CLI coding harness is installed globally and whether it is up to date.
8
+ * Detects whether one CLI coding harness is installed globally and, when enabled, whether it is up to date.
9
9
  *
10
- * Note: `$` is used to indicate that this function is not a pure function - it runs the harness command and queries npm
10
+ * Note: `$` is used to indicate that this function is not a pure function - it runs the harness command and may query npm
11
11
  *
12
12
  * @private internal utility of `promptbookCli`
13
13
  */
14
- export async function $checkHarnessInstallation(definition: HarnessDefinition): Promise<HarnessInstallationStatus> {
14
+ export async function $checkHarnessInstallation(
15
+ definition: HarnessDefinition,
16
+ isHarnessUpdateCheckEnabled = true,
17
+ ): Promise<HarnessInstallationStatus> {
15
18
  const [installedVersion, latestVersion] = await Promise.all([
16
19
  $resolveInstalledHarnessVersion(definition),
17
- $resolveLatestNpmPackageVersion(definition.npmPackageName),
20
+ isHarnessUpdateCheckEnabled ? $resolveLatestNpmPackageVersion(definition.npmPackageName) : Promise.resolve(null),
18
21
  ]);
19
22
 
20
23
  return {
21
24
  definition,
22
- installationState: resolveHarnessInstallationState(installedVersion, latestVersion),
25
+ installationState: resolveHarnessInstallationState(
26
+ installedVersion,
27
+ latestVersion,
28
+ isHarnessUpdateCheckEnabled,
29
+ ),
23
30
  installedVersion,
24
31
  latestVersion,
25
32
  };
@@ -31,11 +38,16 @@ export async function $checkHarnessInstallation(definition: HarnessDefinition):
31
38
  function resolveHarnessInstallationState(
32
39
  installedVersion: string | null,
33
40
  latestVersion: string | null,
41
+ isHarnessUpdateCheckEnabled: boolean,
34
42
  ): HarnessInstallationState {
35
43
  if (installedVersion === null) {
36
44
  return 'not-installed';
37
45
  }
38
46
 
47
+ if (!isHarnessUpdateCheckEnabled) {
48
+ return 'installed';
49
+ }
50
+
39
51
  if (latestVersion === null) {
40
52
  return 'unknown';
41
53
  }
@@ -5,8 +5,8 @@ import type { HarnessDefinition } from './HarnessDefinition';
5
5
  import { getHarnessDefinition } from './HarnessDefinition';
6
6
 
7
7
  /**
8
- * Checks that every given CLI coding harness is installed globally and up to date and offers to
9
- * install or update the ones which are not.
8
+ * Checks that every given CLI coding harness is installed globally and, when enabled, up to date.
9
+ * Offers to install missing harnesses and update outdated ones.
10
10
  *
11
11
  * Harness names which are `undefined`, for example when no `--harness` is selected in a dry run,
12
12
  * are ignored so that every call site can pass its raw selection.
@@ -17,6 +17,7 @@ import { getHarnessDefinition } from './HarnessDefinition';
17
17
  */
18
18
  export async function $ensureHarnessInstallations(
19
19
  harnessNames: ReadonlyArray<PromptRunnerHarnessName | undefined>,
20
+ isHarnessUpdateCheckEnabled = true,
20
21
  ): Promise<void> {
21
22
  const definitions = resolveCheckedHarnessDefinitions(harnessNames);
22
23
 
@@ -25,7 +26,9 @@ export async function $ensureHarnessInstallations(
25
26
  }
26
27
 
27
28
  // Note: Detection of all harnesses runs in parallel, the questions have to be asked one by one
28
- const statuses = await Promise.all(definitions.map((definition) => $checkHarnessInstallation(definition)));
29
+ const statuses = await Promise.all(
30
+ definitions.map((definition) => $checkHarnessInstallation(definition, isHarnessUpdateCheckEnabled)),
31
+ );
29
32
 
30
33
  for (const status of statuses) {
31
34
  await $applyHarnessInstallationStatus(status);