@promptbook/cli 0.114.0-48 → 0.114.0-49

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 (153) hide show
  1. package/README.md +59 -25
  2. package/apps/agents-server/tests/e2e/mockSupabaseServer.cjs +79 -2
  3. package/apps/agents-server/tests/e2e/support/ChatHistoryNavigationSupport.ts +12 -2
  4. package/apps/agents-server/tests/e2e/support/auth.ts +6 -0
  5. package/esm/index.es.js +1327 -478
  6. package/esm/index.es.js.map +1 -1
  7. package/esm/scripts/run-codex-prompts/checks/CheckBeforeMode.d.ts +20 -0
  8. package/esm/scripts/run-codex-prompts/checks/checkRepairInstructions.d.ts +4 -0
  9. package/esm/scripts/run-codex-prompts/checks/createCheckBeforeRepairPrompt.d.ts +9 -0
  10. package/esm/scripts/run-codex-prompts/checks/limitCheckOutput.d.ts +4 -0
  11. package/esm/scripts/run-codex-prompts/checks/projectCheck.d.ts +26 -0
  12. package/esm/scripts/run-codex-prompts/checks/projectScriptReferences.d.ts +7 -0
  13. package/esm/scripts/run-codex-prompts/checks/runCheckBefore.d.ts +26 -0
  14. package/esm/scripts/run-codex-prompts/checks/runPromptCheckCommand.d.ts +16 -0
  15. package/esm/scripts/run-codex-prompts/{testing/runPromptWithTestFeedback.d.ts → checks/runPromptWithCheckFeedback.d.ts} +9 -9
  16. package/esm/scripts/run-codex-prompts/common/resolveCoderContext.d.ts +1 -1
  17. package/esm/scripts/run-codex-prompts/common/resolveCoderProjectContext.d.ts +17 -0
  18. package/esm/scripts/run-codex-prompts/common/resolveInlineOrFileText.d.ts +2 -0
  19. package/esm/scripts/run-codex-prompts/common/runGoScript/quoteBashArgument.d.ts +2 -0
  20. package/esm/scripts/run-codex-prompts/isolation/mapProjectReferenceToWorktree.d.ts +5 -0
  21. package/esm/scripts/run-codex-prompts/main/runPromptRound.d.ts +3 -1
  22. package/esm/scripts/run-codex-prompts/prompts/buildPromptLabel.d.ts +1 -1
  23. package/esm/scripts/run-codex-prompts/prompts/buildPromptLabelForDisplay.d.ts +1 -1
  24. package/esm/scripts/run-codex-prompts/prompts/buildPromptRunTraceContent.d.ts +3 -1
  25. package/esm/scripts/run-codex-prompts/prompts/formatCoderRunSteps.d.ts +2 -2
  26. package/esm/scripts/run-codex-prompts/prompts/listUpcomingTasks.d.ts +1 -1
  27. package/esm/scripts/run-codex-prompts/prompts/printPromptsToBeWritten.d.ts +1 -1
  28. package/esm/scripts/run-codex-prompts/server/buildCoderServerPromptResponse.d.ts +2 -0
  29. package/esm/scripts/run-codex-prompts/team/createCoderTeamPromptRunner.d.ts +1 -1
  30. package/esm/scripts/run-codex-prompts/ui/CoderRunUiState.d.ts +1 -1
  31. package/esm/src/cli/cli-commands/coder/agentCliOptions.d.ts +5 -0
  32. package/esm/src/cli/cli-commands/coder/coderAgentRole.d.ts +5 -0
  33. package/esm/src/cli/cli-commands/coder/coderReferencedArtifacts.d.ts +2 -2
  34. package/esm/src/cli/cli-commands/coder/ensureCoderPackageJsonFile.d.ts +3 -2
  35. package/esm/src/cli/cli-commands/coder/ensureCoderPackageJsonFile.test.d.ts +1 -0
  36. package/esm/src/cli/cli-commands/coder/findLegacyCoderCheckCallers.d.ts +5 -0
  37. package/esm/src/cli/cli-commands/coder/initializeCoderProjectConfiguration.d.ts +6 -0
  38. package/esm/src/cli/cli-commands/coder/mergeStringRecordJsonFile.d.ts +5 -3
  39. package/esm/src/cli/cli-commands/coder/planning/runPlanningSession.d.ts +3 -0
  40. package/esm/src/cli/cli-commands/coder/prepareCoderPackageJsonScripts.d.ts +12 -0
  41. package/esm/src/cli/cli-commands/coder.d.ts +1 -1
  42. package/esm/src/cli/cli-commands/common/coderCheckCliOptions.d.ts +21 -0
  43. package/esm/src/cli/cli-commands/common/coderCheckCliOptions.test.d.ts +1 -0
  44. package/esm/src/cli/cli-commands/common/projectCliOptions.d.ts +30 -0
  45. package/esm/src/cli/cli-commands/common/projectCliOptions.test.d.ts +1 -0
  46. package/esm/src/cli/cli-commands/common/promptbook-cli/$checkPromptbookCliInstallations.d.ts +1 -1
  47. package/esm/src/cli/cli-commands/common/promptbook-cli/$ensurePromptbookCliInstallations.d.ts +1 -1
  48. package/esm/src/cli/cli-commands/common/promptbook-cli/$resolvePromptbookCliInstallations.d.ts +1 -1
  49. package/esm/src/cli/cli-commands/common/promptbook-cli/$updatePromptbookCliInstallation.d.ts +1 -1
  50. package/esm/src/version.d.ts +1 -1
  51. package/package.json +1 -1
  52. package/src/_packages/cli.readme.md +15 -0
  53. package/src/cli/cli-commands/agent/chat.ts +11 -2
  54. package/src/cli/cli-commands/agent/exec.ts +11 -2
  55. package/src/cli/cli-commands/agent-folder/init.ts +6 -2
  56. package/src/cli/cli-commands/agent-folder/initializeAgentRunnerCommand.ts +9 -2
  57. package/src/cli/cli-commands/coder/add.ts +7 -1
  58. package/src/cli/cli-commands/coder/agentCliOptions.ts +11 -1
  59. package/src/cli/cli-commands/coder/coderAgentRole.ts +6 -0
  60. package/src/cli/cli-commands/coder/coderReferencedArtifacts.ts +3 -3
  61. package/src/cli/cli-commands/coder/ensureCoderPackageJsonFile.ts +22 -3
  62. package/src/cli/cli-commands/coder/find-fresh-emoji-tags.ts +3 -0
  63. package/src/cli/cli-commands/coder/find-refactor-candidates.ts +3 -0
  64. package/src/cli/cli-commands/coder/find-unwritten.ts +3 -0
  65. package/src/cli/cli-commands/coder/findLegacyCoderCheckCallers.ts +87 -0
  66. package/src/cli/cli-commands/coder/generate-boilerplates.ts +3 -0
  67. package/src/cli/cli-commands/coder/getDefaultCoderPackageJsonScripts.ts +6 -7
  68. package/src/cli/cli-commands/coder/init.ts +8 -2
  69. package/src/cli/cli-commands/coder/initializeCoderProjectConfiguration.ts +16 -4
  70. package/src/cli/cli-commands/coder/list.ts +6 -1
  71. package/src/cli/cli-commands/coder/mergeStringRecordJsonFile.ts +14 -4
  72. package/src/cli/cli-commands/coder/ping.ts +7 -1
  73. package/src/cli/cli-commands/coder/plan.ts +17 -5
  74. package/src/cli/cli-commands/coder/planning/README.md +5 -3
  75. package/src/cli/cli-commands/coder/planning/fixtures/coding.cjs +22 -0
  76. package/src/cli/cli-commands/coder/planning/runPlanningSession.ts +14 -3
  77. package/src/cli/cli-commands/coder/planning/savePlanningChanges.ts +1 -3
  78. package/src/cli/cli-commands/coder/prepareCoderPackageJsonScripts.ts +304 -0
  79. package/src/cli/cli-commands/coder/printInitializationSummary.ts +5 -1
  80. package/src/cli/cli-commands/coder/promptsReadmeTemplate.ts +31 -13
  81. package/src/cli/cli-commands/coder/run.ts +31 -65
  82. package/src/cli/cli-commands/coder/server.ts +21 -39
  83. package/src/cli/cli-commands/coder/verify.ts +3 -0
  84. package/src/cli/cli-commands/coder.ts +2 -2
  85. package/src/cli/cli-commands/common/coderCheckCliOptions.ts +107 -0
  86. package/src/cli/cli-commands/common/projectCliOptions.ts +78 -0
  87. package/src/cli/cli-commands/common/promptbook-cli/$checkPromptbookCliInstallations.ts +4 -2
  88. package/src/cli/cli-commands/common/promptbook-cli/$ensurePromptbookCliInstallations.ts +3 -2
  89. package/src/cli/cli-commands/common/promptbook-cli/$resolvePromptbookCliInstallations.ts +8 -4
  90. package/src/cli/cli-commands/common/promptbook-cli/$updatePromptbookCliInstallation.ts +5 -2
  91. package/src/cli/cli-commands/common/validateCoderRunOptions.ts +12 -5
  92. package/src/cli/cli-commands/common/workspaceRepository.ts +7 -19
  93. package/src/cli/cli-commands/common/workspaceRepositoryCliOptions.ts +2 -0
  94. package/src/execution/translation/automatic-translate/translateMessages.ts +1 -3
  95. package/src/other/templates/getTemplatesPipelineCollection.ts +1074 -1210
  96. package/src/version.ts +2 -2
  97. package/src/versions.txt +1 -0
  98. package/umd/index.umd.js +1324 -475
  99. package/umd/index.umd.js.map +1 -1
  100. package/umd/scripts/run-codex-prompts/checks/CheckBeforeMode.d.ts +20 -0
  101. package/umd/scripts/run-codex-prompts/checks/checkRepairInstructions.d.ts +4 -0
  102. package/umd/scripts/run-codex-prompts/checks/createCheckBeforeRepairPrompt.d.ts +9 -0
  103. package/umd/scripts/run-codex-prompts/checks/limitCheckOutput.d.ts +4 -0
  104. package/umd/scripts/run-codex-prompts/checks/projectCheck.d.ts +26 -0
  105. package/umd/scripts/run-codex-prompts/checks/projectScriptReferences.d.ts +7 -0
  106. package/umd/scripts/run-codex-prompts/checks/runCheckBefore.d.ts +26 -0
  107. package/umd/scripts/run-codex-prompts/checks/runPromptCheckCommand.d.ts +16 -0
  108. package/umd/scripts/run-codex-prompts/{testing/runPromptWithTestFeedback.d.ts → checks/runPromptWithCheckFeedback.d.ts} +9 -9
  109. package/umd/scripts/run-codex-prompts/common/resolveCoderContext.d.ts +1 -1
  110. package/umd/scripts/run-codex-prompts/common/resolveCoderProjectContext.d.ts +17 -0
  111. package/umd/scripts/run-codex-prompts/common/resolveInlineOrFileText.d.ts +2 -0
  112. package/umd/scripts/run-codex-prompts/common/runGoScript/quoteBashArgument.d.ts +2 -0
  113. package/umd/scripts/run-codex-prompts/isolation/mapProjectReferenceToWorktree.d.ts +5 -0
  114. package/umd/scripts/run-codex-prompts/main/runPromptRound.d.ts +3 -1
  115. package/umd/scripts/run-codex-prompts/prompts/buildPromptLabel.d.ts +1 -1
  116. package/umd/scripts/run-codex-prompts/prompts/buildPromptLabelForDisplay.d.ts +1 -1
  117. package/umd/scripts/run-codex-prompts/prompts/buildPromptRunTraceContent.d.ts +3 -1
  118. package/umd/scripts/run-codex-prompts/prompts/formatCoderRunSteps.d.ts +2 -2
  119. package/umd/scripts/run-codex-prompts/prompts/listUpcomingTasks.d.ts +1 -1
  120. package/umd/scripts/run-codex-prompts/prompts/printPromptsToBeWritten.d.ts +1 -1
  121. package/umd/scripts/run-codex-prompts/server/buildCoderServerPromptResponse.d.ts +2 -0
  122. package/umd/scripts/run-codex-prompts/team/createCoderTeamPromptRunner.d.ts +1 -1
  123. package/umd/scripts/run-codex-prompts/ui/CoderRunUiState.d.ts +1 -1
  124. package/umd/src/cli/cli-commands/coder/agentCliOptions.d.ts +5 -0
  125. package/umd/src/cli/cli-commands/coder/coderAgentRole.d.ts +5 -0
  126. package/umd/src/cli/cli-commands/coder/coderReferencedArtifacts.d.ts +2 -2
  127. package/umd/src/cli/cli-commands/coder/ensureCoderPackageJsonFile.d.ts +3 -2
  128. package/umd/src/cli/cli-commands/coder/ensureCoderPackageJsonFile.test.d.ts +1 -0
  129. package/umd/src/cli/cli-commands/coder/findLegacyCoderCheckCallers.d.ts +5 -0
  130. package/umd/src/cli/cli-commands/coder/initializeCoderProjectConfiguration.d.ts +6 -0
  131. package/umd/src/cli/cli-commands/coder/mergeStringRecordJsonFile.d.ts +5 -3
  132. package/umd/src/cli/cli-commands/coder/planning/runPlanningSession.d.ts +3 -0
  133. package/umd/src/cli/cli-commands/coder/prepareCoderPackageJsonScripts.d.ts +12 -0
  134. package/umd/src/cli/cli-commands/coder.d.ts +1 -1
  135. package/umd/src/cli/cli-commands/common/coderCheckCliOptions.d.ts +21 -0
  136. package/umd/src/cli/cli-commands/common/coderCheckCliOptions.test.d.ts +1 -0
  137. package/umd/src/cli/cli-commands/common/projectCliOptions.d.ts +30 -0
  138. package/umd/src/cli/cli-commands/common/projectCliOptions.test.d.ts +1 -0
  139. package/umd/src/cli/cli-commands/common/promptbook-cli/$checkPromptbookCliInstallations.d.ts +1 -1
  140. package/umd/src/cli/cli-commands/common/promptbook-cli/$ensurePromptbookCliInstallations.d.ts +1 -1
  141. package/umd/src/cli/cli-commands/common/promptbook-cli/$resolvePromptbookCliInstallations.d.ts +1 -1
  142. package/umd/src/cli/cli-commands/common/promptbook-cli/$updatePromptbookCliInstallation.d.ts +1 -1
  143. package/umd/src/version.d.ts +1 -1
  144. package/esm/scripts/run-codex-prompts/testing/TestBeforeMode.d.ts +0 -16
  145. package/esm/scripts/run-codex-prompts/testing/createTestBeforeRepairPrompt.d.ts +0 -9
  146. package/esm/scripts/run-codex-prompts/testing/limitTestOutput.d.ts +0 -4
  147. package/esm/scripts/run-codex-prompts/testing/runPromptTestCommand.d.ts +0 -14
  148. package/esm/scripts/run-codex-prompts/testing/runTestBefore.d.ts +0 -24
  149. package/umd/scripts/run-codex-prompts/testing/TestBeforeMode.d.ts +0 -16
  150. package/umd/scripts/run-codex-prompts/testing/createTestBeforeRepairPrompt.d.ts +0 -9
  151. package/umd/scripts/run-codex-prompts/testing/limitTestOutput.d.ts +0 -4
  152. package/umd/scripts/run-codex-prompts/testing/runPromptTestCommand.d.ts +0 -14
  153. package/umd/scripts/run-codex-prompts/testing/runTestBefore.d.ts +0 -24
@@ -1,3 +1,9 @@
1
+ import {
2
+ addProjectPathOption,
3
+ addProjectContextOption,
4
+ normalizeProjectCliOptions,
5
+ resolveProjectDirectory,
6
+ } from '../common/projectCliOptions';
1
7
  import type {
2
8
  Command as Program /* <- Note: [🔸] Using Program because Command is misleading name */,
3
9
  } from 'commander';
@@ -32,10 +38,13 @@ export function $initializeAgentChatCommand(program: Program): $side_effect {
32
38
  command.requiredOption('--agent <agent-book-path>', 'Path to the agent .book file');
33
39
  addPromptRunnerSelectionOptions(command);
34
40
  addPromptRunnerRuntimeOptions(command);
35
- command.option('--context <context-or-file>', 'Append extra context either inline or from a file path');
41
+ addProjectPathOption(command);
42
+ addProjectContextOption(command);
36
43
 
37
44
  command.action(
38
45
  handleActionErrors(async (cliOptions, commandProgram) => {
46
+ const projectOptions = normalizeProjectCliOptions(cliOptions);
47
+ const projectPath = await resolveProjectDirectory(projectOptions.projectDirectory);
39
48
  const options = cliOptions as AgentCommandCliOptions;
40
49
  const runnerOptions = normalizeAgentCommandRunnerOptions(options, commandProgram as Program);
41
50
  const { runAgentChat } = await import('../../../../scripts/run-agent-chat/runAgentChat');
@@ -43,7 +52,7 @@ export function $initializeAgentChatCommand(program: Program): $side_effect {
43
52
  await runAgentChat({
44
53
  agentPath: resolveRequiredAgentPath(options),
45
54
  context: options.context,
46
- currentWorkingDirectory: process.cwd(),
55
+ currentWorkingDirectory: projectPath,
47
56
  ...runnerOptions,
48
57
  });
49
58
  }),
@@ -1,3 +1,9 @@
1
+ import {
2
+ addProjectPathOption,
3
+ addProjectContextOption,
4
+ normalizeProjectCliOptions,
5
+ resolveProjectDirectory,
6
+ } from '../common/projectCliOptions';
1
7
  import type {
2
8
  Command as Program /* <- Note: [🔸] Using Program because Command is misleading name */,
3
9
  } from 'commander';
@@ -37,10 +43,13 @@ export function $initializeAgentExecCommand(program: Program): $side_effect {
37
43
  command.requiredOption('--message <message>', 'User message to send to the agent');
38
44
  addPromptRunnerSelectionOptions(command);
39
45
  addPromptRunnerRuntimeOptions(command);
40
- command.option('--context <context-or-file>', 'Append extra context either inline or from a file path');
46
+ addProjectPathOption(command);
47
+ addProjectContextOption(command);
41
48
 
42
49
  command.action(
43
50
  handleActionErrors(async (cliOptions, commandProgram) => {
51
+ const projectOptions = normalizeProjectCliOptions(cliOptions);
52
+ const projectPath = await resolveProjectDirectory(projectOptions.projectDirectory);
44
53
  const options = cliOptions as AgentCommandCliOptions;
45
54
  const runnerOptions = normalizeAgentCommandRunnerOptions(options, commandProgram as Program);
46
55
  const { runAgentExec } = await import('../../../../scripts/run-agent-chat/runAgentExec');
@@ -49,7 +58,7 @@ export function $initializeAgentExecCommand(program: Program): $side_effect {
49
58
  agentPath: resolveRequiredAgentPath(options),
50
59
  context: options.context,
51
60
  message: resolveRequiredAgentMessage(options),
52
- currentWorkingDirectory: process.cwd(),
61
+ currentWorkingDirectory: projectPath,
53
62
  ...runnerOptions,
54
63
  });
55
64
  }),
@@ -1,3 +1,4 @@
1
+ import { addProjectPathOption, normalizeProjectCliOptions, resolveProjectDirectory } from '../common/projectCliOptions';
1
2
  import type {
2
3
  Command as Program /* <- Note: [🔸] Using Program because Command is misleading name */,
3
4
  } from 'commander';
@@ -36,9 +37,12 @@ export function $initializeAgentInitCommand(program: Program): $side_effect {
36
37
  `),
37
38
  );
38
39
 
40
+ addProjectPathOption(command);
39
41
  command.action(
40
- handleActionErrors(async () => {
41
- const summary = await initializeAgentProjectConfiguration(process.cwd());
42
+ handleActionErrors(async (cliOptions) => {
43
+ const projectOptions = normalizeProjectCliOptions(cliOptions);
44
+ const projectPath = await resolveProjectDirectory(projectOptions.projectDirectory);
45
+ const summary = await initializeAgentProjectConfiguration(projectPath);
42
46
  printAgentInitializationSummary(summary);
43
47
  }),
44
48
  );
@@ -1,3 +1,4 @@
1
+ import { addProjectPathOption, normalizeProjectCliOptions, resolveProjectDirectory } from '../common/projectCliOptions';
1
2
  import colors from 'colors';
2
3
  import type {
3
4
  Command as Program /* <- Note: [🔸] Using Program because Command is misleading name */,
@@ -61,12 +62,18 @@ export function $initializeAgentRunnerCommand(
61
62
 
62
63
  addPromptRunnerSelectionOptions(command);
63
64
  addPromptRunnerExecutionOptions(command);
65
+ addProjectPathOption(command);
64
66
  options.configureCommand?.(command);
65
67
 
66
68
  command.action(
67
69
  handleActionErrors(
68
70
  async (cliOptions) => {
69
- const runOptions = createAgentRunOptionsFromCliOptions(cliOptions as AgentRunCliOptions);
71
+ const projectOptions = normalizeProjectCliOptions(cliOptions);
72
+ const projectPath = await resolveProjectDirectory(projectOptions.projectDirectory);
73
+ const runOptions = {
74
+ ...createAgentRunOptionsFromCliOptions(cliOptions as AgentRunCliOptions),
75
+ projectPath,
76
+ };
70
77
  const execute = await options.loadExecutor();
71
78
 
72
79
  if (options.executionMode === 'watch') {
@@ -76,7 +83,7 @@ export function $initializeAgentRunnerCommand(
76
83
 
77
84
  await runPersistentAgentWatch({
78
85
  commandDisplayName: `ptbk agent-folder ${options.commandName}`,
79
- logDirectoryPath: process.cwd(),
86
+ logDirectoryPath: projectPath,
80
87
  runWatch: async () => {
81
88
  await execute(runOptions);
82
89
  },
@@ -18,6 +18,7 @@ import {
18
18
  } from '../common/coderGitSyncCliOptions';
19
19
  import { $preflightWorkspaceRepository } from '../common/workspaceRepository';
20
20
  import { addWorkspaceRepositoryOptions } from '../common/workspaceRepositoryCliOptions';
21
+ import { normalizeProjectCliOptions } from '../common/projectCliOptions';
21
22
  import { handleActionErrors } from '../common/handleActionErrors';
22
23
  import {
23
24
  addQuestionsOption,
@@ -105,6 +106,7 @@ export function $initializeCoderAddCommand(program: Program): $side_effect {
105
106
 
106
107
  command.action(
107
108
  handleActionErrors(async (descriptionArgument: string | undefined, cliOptions) => {
109
+ const projectOptions = normalizeProjectCliOptions(cliOptions);
108
110
  const { priority, template: templateOption } = cliOptions as {
109
111
  readonly priority: number;
110
112
  readonly template?: string;
@@ -112,7 +114,11 @@ export function $initializeCoderAddCommand(program: Program): $side_effect {
112
114
 
113
115
  const gitSync = normalizeCoderGitSyncCliOptions(cliOptions as CoderGitSyncCliOptions);
114
116
  const questionsOptions = normalizeQuestionsCliOptions(cliOptions as QuestionsCliOptions);
115
- const workspace = await $preflightWorkspaceRepository({ policy: 'mutate', ...questionsOptions });
117
+ const workspace = await $preflightWorkspaceRepository({
118
+ ...projectOptions,
119
+ policy: 'mutate',
120
+ ...questionsOptions,
121
+ });
116
122
  const { projectPath } = workspace;
117
123
 
118
124
  const description = await resolveCoderPromptDescription(descriptionArgument, questionsOptions);
@@ -1,7 +1,8 @@
1
1
  import type {
2
2
  Command as Program /* <- Note: [🔸] Using Program because Command is misleading name */,
3
3
  } from 'commander';
4
- import { CODER_DEFAULT_AGENT_BOOK_PATHS, type CoderAgentRole } from './coderAgentRole';
4
+ import { addProjectContextOption } from '../common/projectCliOptions';
5
+ import { CODER_DEFAULT_AGENT_BOOK_PATHS, DEFAULT_CODER_AGENT_ROLE, type CoderAgentRole } from './coderAgentRole';
5
6
 
6
7
  /**
7
8
  * Commander option bag for an optional Book agent used by coder commands.
@@ -34,5 +35,14 @@ export function addCoderAgentOption(command: Program, defaultRole?: CoderAgentRo
34
35
  command.option('--agent <agent-book-path>', `${CODER_AGENT_OPTION_DESCRIPTION}. ${defaultDescription}`);
35
36
  }
36
37
 
38
+ /**
39
+ * Registers the shared optional persona and context for single-agent execution or authoring.
40
+ * @private internal CLI registration helper
41
+ */
42
+ export function addCoderExecutionOptions(command: Program): void {
43
+ addCoderAgentOption(command, DEFAULT_CODER_AGENT_ROLE);
44
+ addProjectContextOption(command);
45
+ }
46
+
37
47
  // Note: [🟡] Code for CLI coder agent options should never be published outside of `@promptbook/cli`
38
48
  // Note: [💞] Ignore a discrepancy between file name and exported helper names
@@ -19,4 +19,10 @@ export const CODER_DEFAULT_AGENT_BOOK_PATHS = {
19
19
  */
20
20
  export type CoderAgentRole = keyof typeof CODER_DEFAULT_AGENT_BOOK_PATHS;
21
21
 
22
+ /**
23
+ * Shared persona for optional single-agent execution and authoring; capabilities belong to the command.
24
+ * @private internal constant of project CLI commands
25
+ */
26
+ export const DEFAULT_CODER_AGENT_ROLE: CoderAgentRole = 'developer';
27
+
22
28
  // Note: [💞] Ignore a discrepancy between file name and exported helper names
@@ -75,9 +75,9 @@ const CODER_REFERENCED_ARTIFACT_DEFINITIONS: ReadonlyArray<CoderReferencedArtifa
75
75
  ];
76
76
 
77
77
  /**
78
- * Initializes artifacts referenced by freshly added scripts. Default Books are ensured separately.
78
+ * Initializes essential context and artifacts referenced by freshly added scripts. Books are ensured separately.
79
79
  *
80
- * For artifacts other than role Books, an existing project script does not cause default files to be created.
80
+ * AGENTS.md is essential even when every script already exists or uses implicit defaults.
81
81
  *
82
82
  * @private function of `initializeCoderProjectConfiguration`
83
83
  */
@@ -88,7 +88,7 @@ export async function ensureCoderReferencedArtifacts(
88
88
  const ensuredArtifacts: Array<EnsuredCoderReferencedArtifact> = [];
89
89
 
90
90
  for (const { relativeFilePath, ensureArtifactFile } of CODER_REFERENCED_ARTIFACT_DEFINITIONS) {
91
- if (!referencedArtifactPaths.has(relativeFilePath)) {
91
+ if (relativeFilePath !== AGENTS_FILE_PATH && !referencedArtifactPaths.has(relativeFilePath)) {
92
92
  ensuredArtifacts.push({ relativeFilePath, status: 'not-referenced' });
93
93
  continue;
94
94
  }
@@ -1,6 +1,8 @@
1
1
  import { getDefaultCoderPackageJsonScripts } from './getDefaultCoderPackageJsonScripts';
2
2
  import type { MergedStringRecordJsonFile } from './mergeStringRecordJsonFile';
3
3
  import { mergeStringRecordJsonFile } from './mergeStringRecordJsonFile';
4
+ import { prepareCoderPackageJsonScripts, type PreparedCoderPackageJsonScripts } from './prepareCoderPackageJsonScripts';
5
+ import { findLegacyCoderCheckCallers } from './findLegacyCoderCheckCallers';
4
6
 
5
7
  /**
6
8
  * Relative path to `package.json` in the initialized project.
@@ -10,17 +12,34 @@ const PACKAGE_JSON_FILE_PATH = 'package.json';
10
12
  /**
11
13
  * Ensures `package.json` contains the standalone Promptbook coder helper scripts.
12
14
  *
13
- * Scripts which the project already defines are kept as they are, only missing ones are added.
15
+ * Project definitions are preserved; missing scripts and narrowly recognized generated migrations are applied.
14
16
  *
15
17
  * @private function of `initializeCoderProjectConfiguration`
16
18
  */
17
- export async function ensureCoderPackageJsonFile(projectPath: string): Promise<MergedStringRecordJsonFile> {
18
- return mergeStringRecordJsonFile({
19
+ export async function ensureCoderPackageJsonFile(
20
+ projectPath: string,
21
+ ): Promise<MergedStringRecordJsonFile & Omit<PreparedCoderPackageJsonScripts, 'scripts'>> {
22
+ let preparation: PreparedCoderPackageJsonScripts;
23
+ const mergedFile = await mergeStringRecordJsonFile({
19
24
  projectPath,
20
25
  relativeFilePath: PACKAGE_JSON_FILE_PATH,
21
26
  fieldPath: 'scripts',
22
27
  nextEntries: getDefaultCoderPackageJsonScripts(),
28
+ transformExistingEntries: async (entries) => {
29
+ const legacyNames = Object.keys(entries).filter(
30
+ (name) => name === 'test-for-ptbk-coder' || name === 'check-for-ptbk-coder',
31
+ );
32
+ const externalCallers = await findLegacyCoderCheckCallers(projectPath, legacyNames);
33
+ preparation = prepareCoderPackageJsonScripts(entries, externalCallers);
34
+ return preparation.scripts;
35
+ },
23
36
  });
37
+ return {
38
+ ...mergedFile,
39
+ checkScriptSummary: preparation!.checkScriptSummary,
40
+ migrationInstructions: preparation!.migrationInstructions,
41
+ isCheckConfigured: preparation!.isCheckConfigured,
42
+ };
24
43
  }
25
44
 
26
45
  // Note: [🟡] Code for coder init package.json bootstrapping [ensureCoderPackageJsonFile](src/cli/cli-commands/coder/ensureCoderPackageJsonFile.ts) should never be published outside of `@promptbook/cli`
@@ -6,6 +6,7 @@ import spaceTrim from 'spacetrim';
6
6
  import type { $side_effect } from '../../../utils/organization/$side_effect';
7
7
  import { $preflightWorkspaceRepository } from '../common/workspaceRepository';
8
8
  import { addWorkspaceRepositoryOptions } from '../common/workspaceRepositoryCliOptions';
9
+ import { normalizeProjectCliOptions } from '../common/projectCliOptions';
9
10
  import { handleActionErrors } from '../common/handleActionErrors';
10
11
 
11
12
  /**
@@ -31,7 +32,9 @@ export function $initializeCoderFindFreshEmojiTagCommand(program: Program): $sid
31
32
 
32
33
  command.action(
33
34
  handleActionErrors(async (cliOptions) => {
35
+ const projectOptions = normalizeProjectCliOptions(cliOptions);
34
36
  const workspace = await $preflightWorkspaceRepository({
37
+ ...projectOptions,
35
38
  policy: 'read-only',
36
39
  isAskingQuestionsEnabled: cliOptions.questions,
37
40
  });
@@ -15,6 +15,7 @@ import type { $side_effect } from '../../../utils/organization/$side_effect';
15
15
  import { createPositiveIntegerOptionParser } from '../common/createPositiveIntegerOptionParser';
16
16
  import { $preflightWorkspaceRepository } from '../common/workspaceRepository';
17
17
  import { addWorkspaceRepositoryOptions } from '../common/workspaceRepositoryCliOptions';
18
+ import { normalizeProjectCliOptions } from '../common/projectCliOptions';
18
19
  import { handleActionErrors } from '../common/handleActionErrors';
19
20
 
20
21
  /**
@@ -57,12 +58,14 @@ export function $initializeCoderFindRefactorCandidatesCommand(program: Program):
57
58
 
58
59
  command.action(
59
60
  handleActionErrors(async (cliOptions) => {
61
+ const projectOptions = normalizeProjectCliOptions(cliOptions);
60
62
  const { level = DEFAULT_REFACTOR_CANDIDATE_LEVEL, limit } = cliOptions as {
61
63
  readonly level?: RefactorCandidateLevel;
62
64
  readonly limit?: number;
63
65
  };
64
66
 
65
67
  const workspace = await $preflightWorkspaceRepository({
68
+ ...projectOptions,
66
69
  policy: 'mutate',
67
70
  isAskingQuestionsEnabled: cliOptions.questions,
68
71
  });
@@ -4,6 +4,7 @@ import { assertsError } from '../../../errors/assertsError';
4
4
  import type { $side_effect } from '../../../utils/organization/$side_effect';
5
5
  import { $preflightWorkspaceRepository } from '../common/workspaceRepository';
6
6
  import { addWorkspaceRepositoryOptions } from '../common/workspaceRepositoryCliOptions';
7
+ import { normalizeProjectCliOptions } from '../common/projectCliOptions';
7
8
  import { handleActionErrors } from '../common/handleActionErrors';
8
9
 
9
10
  /**
@@ -22,11 +23,13 @@ export function $initializeCoderFindUnwrittenCommand(program: Program): $side_ef
22
23
 
23
24
  command.action(
24
25
  handleActionErrors(async (cliOptions) => {
26
+ const projectOptions = normalizeProjectCliOptions(cliOptions);
25
27
  const { priority = 0 } = cliOptions as {
26
28
  readonly priority?: number;
27
29
  };
28
30
 
29
31
  const workspace = await $preflightWorkspaceRepository({
32
+ ...projectOptions,
30
33
  policy: 'read-only',
31
34
  isAskingQuestionsEnabled: cliOptions.questions,
32
35
  });
@@ -0,0 +1,87 @@
1
+ import { readFile, readdir } from 'fs/promises';
2
+ import type { Dirent } from 'fs';
3
+ import { join } from 'path';
4
+
5
+ // cspell:ignore Procfile
6
+
7
+ /** Runtime/dependency/history directories do not contain maintained project callers. */
8
+ const IGNORED_CALLER_DIRECTORIES = new Set([
9
+ 'node_modules',
10
+ '.git',
11
+ '.promptbook',
12
+ '.next',
13
+ '.tmp',
14
+ 'dist',
15
+ 'build',
16
+ 'coverage',
17
+ ]);
18
+
19
+ /** Only Coder's known task-history paths are excluded; the same names elsewhere can hold active callers. */
20
+ const IGNORED_CALLER_HISTORY_PATHS = new Set(['prompts/done', 'prompts/traces']);
21
+
22
+ /** Source and workflow files whose references must be preserved during npm script migration. */
23
+ const CALLER_FILE_PATTERN = /\.(?:[cm]?jsx?|tsx?|json|ya?ml|md|sh|bash|ps1|cmd|bat|py|toml|xml|groovy)$/iu;
24
+
25
+ /** Conventional workflow files which have no filename extension. */
26
+ const CALLER_FILE_NAMES = new Set(['Makefile', 'Dockerfile', 'Jenkinsfile', 'Procfile']);
27
+
28
+ /**
29
+ * Finds external project callers conservatively; it never rewrites their shell expressions.
30
+ * @private migration safety check for the Coder initializer
31
+ */
32
+ export async function findLegacyCoderCheckCallers(
33
+ projectPath: string,
34
+ legacyNames: ReadonlyArray<string>,
35
+ ): Promise<Readonly<Record<string, ReadonlyArray<string>>>> {
36
+ const callers: Record<string, string[]> = Object.fromEntries(legacyNames.map((name) => [name, []]));
37
+ if (legacyNames.length === 0) return callers;
38
+
39
+ /** Scans maintained text files without following symlinks or inspecting runtime state. */
40
+ async function visit(relativeDirectory: string): Promise<void> {
41
+ let entries: Dirent[];
42
+ try {
43
+ entries = await readdir(join(projectPath, relativeDirectory), { withFileTypes: true });
44
+ } catch {
45
+ for (const legacyName of legacyNames)
46
+ callers[legacyName]!.push(`${relativeDirectory} (unreadable directory; review manually)`);
47
+ return;
48
+ }
49
+ for (const entry of entries) {
50
+ const relativePath = relativeDirectory ? `${relativeDirectory}/${entry.name}` : entry.name;
51
+ if (entry.isSymbolicLink()) {
52
+ if (!IGNORED_CALLER_DIRECTORIES.has(entry.name)) {
53
+ for (const legacyName of legacyNames)
54
+ callers[legacyName]!.push(`${relativePath} (symbolic link; review manually)`);
55
+ }
56
+ continue;
57
+ }
58
+ if (entry.isDirectory()) {
59
+ if (!IGNORED_CALLER_DIRECTORIES.has(entry.name) && !IGNORED_CALLER_HISTORY_PATHS.has(relativePath))
60
+ await visit(relativePath);
61
+ } else if (entry.isFile() && (CALLER_FILE_PATTERN.test(entry.name) || CALLER_FILE_NAMES.has(entry.name))) {
62
+ try {
63
+ let content = await readFile(join(projectPath, relativePath), 'utf-8');
64
+ if (relativePath === 'package.json') {
65
+ // Script callers are already handled by the merge. Other project-owned metadata may also
66
+ // reference a script, so it must be included when deciding whether removal is safe.
67
+ const packageMetadata = JSON.parse(content);
68
+ delete packageMetadata.scripts;
69
+ content = JSON.stringify(packageMetadata);
70
+ }
71
+ for (const legacyName of legacyNames) {
72
+ if (content.includes(legacyName))
73
+ callers[legacyName]!.push(
74
+ relativePath === 'package.json' ? 'package.json (outside scripts)' : relativePath,
75
+ );
76
+ }
77
+ } catch {
78
+ // An unreadable file cannot prove that deletion is safe. Keep the entry and explain why.
79
+ for (const legacyName of legacyNames)
80
+ callers[legacyName]!.push(`${relativePath} (unreadable; review manually)`);
81
+ }
82
+ }
83
+ }
84
+ }
85
+ await visit('');
86
+ return callers;
87
+ }
@@ -14,6 +14,7 @@ import {
14
14
  } from '../common/coderGitSyncCliOptions';
15
15
  import { $preflightWorkspaceRepository } from '../common/workspaceRepository';
16
16
  import { addWorkspaceRepositoryOptions } from '../common/workspaceRepositoryCliOptions';
17
+ import { normalizeProjectCliOptions } from '../common/projectCliOptions';
17
18
  import { handleActionErrors } from '../common/handleActionErrors';
18
19
  import type { BoilerplateCount } from './boilerplateCount';
19
20
  import {
@@ -65,6 +66,7 @@ export function $initializeCoderGenerateBoilerplatesCommand(program: Program): $
65
66
 
66
67
  command.action(
67
68
  handleActionErrors(async (cliOptions) => {
69
+ const projectOptions = normalizeProjectCliOptions(cliOptions);
68
70
  const { count: countOption, template: templateOption } = cliOptions as {
69
71
  readonly count: string;
70
72
  readonly template?: string;
@@ -73,6 +75,7 @@ export function $initializeCoderGenerateBoilerplatesCommand(program: Program): $
73
75
  const boilerplateCount = parseBoilerplateCount(countOption);
74
76
  const gitSync = normalizeCoderGitSyncCliOptions(cliOptions as CoderGitSyncCliOptions);
75
77
  const workspace = await $preflightWorkspaceRepository({
78
+ ...projectOptions,
76
79
  policy: 'mutate',
77
80
  isAskingQuestionsEnabled: cliOptions.questions,
78
81
  });
@@ -2,13 +2,13 @@ import { AGENTS_FILE_PATH } from './agentsFile';
2
2
  import { DEFAULT_BOILERPLATE_COUNT_OPTION_VALUE } from './boilerplateCount';
3
3
  import { COMMON_PROMPT_TEMPLATE_FILE_PATH } from './boilerplateTemplates';
4
4
  import { CODER_DEVELOPER_AGENT_FILE_PATH } from './ensureCoderDeveloperAgentFile';
5
- import { CODER_PLANNER_AGENT_FILE_PATH } from './ensureCoderRoleAgentFile';
6
5
  import { formatDisplayPath } from './formatDisplayPath';
6
+ import { CHECK_SETUP_PLACEHOLDER_COMMAND } from '../../../../scripts/run-codex-prompts/checks/projectCheck';
7
7
 
8
8
  /**
9
9
  * Name of the npm script which `ptbk coder run` uses to verify the project.
10
10
  */
11
- const CODER_TEST_SCRIPT_NAME = 'test-for-ptbk-coder';
11
+ const CHECK_SCRIPT_NAME = 'check';
12
12
 
13
13
  /**
14
14
  * One npm script initialized by `ptbk coder init`.
@@ -53,14 +53,13 @@ const DEFAULT_CODER_PACKAGE_JSON_SCRIPT_DEFINITIONS: ReadonlyArray<CoderPackageJ
53
53
  {
54
54
  scriptName: 'coder:plan',
55
55
  scriptCommand: 'npx ptbk coder plan --harness openai-codex',
56
- referencedArtifactPaths: [CODER_PLANNER_AGENT_FILE_PATH, COMMON_PROMPT_TEMPLATE_FILE_PATH],
56
+ referencedArtifactPaths: [CODER_DEVELOPER_AGENT_FILE_PATH, AGENTS_FILE_PATH, COMMON_PROMPT_TEMPLATE_FILE_PATH],
57
57
  },
58
58
  {
59
59
  scriptName: 'coder:run',
60
60
  scriptCommand: [
61
61
  'npx ptbk coder run --harness openai-codex --thinking-level max',
62
- `--context ${formatDisplayPath(AGENTS_FILE_PATH)}`,
63
- `--test "npm run ${CODER_TEST_SCRIPT_NAME}" --test-before yes-and-fix`,
62
+ `--check "npm run ${CHECK_SCRIPT_NAME}" --check-before yes-and-fix`,
64
63
  ].join(' '),
65
64
  referencedArtifactPaths: [CODER_DEVELOPER_AGENT_FILE_PATH, AGENTS_FILE_PATH],
66
65
  },
@@ -73,8 +72,8 @@ const DEFAULT_CODER_PACKAGE_JSON_SCRIPT_DEFINITIONS: ReadonlyArray<CoderPackageJ
73
72
  {
74
73
  // Note: The verification command of `coder:run` is a project-owned script, so every project can decide
75
74
  // what "verified" means without touching the `coder:run` script itself
76
- scriptName: CODER_TEST_SCRIPT_NAME,
77
- scriptCommand: 'npm test',
75
+ scriptName: CHECK_SCRIPT_NAME,
76
+ scriptCommand: CHECK_SETUP_PLACEHOLDER_COMMAND,
78
77
  referencedArtifactPaths: [],
79
78
  },
80
79
  ];
@@ -14,6 +14,7 @@ import {
14
14
  } from '../common/coderGitSyncCliOptions';
15
15
  import { $preflightWorkspaceRepository } from '../common/workspaceRepository';
16
16
  import { addWorkspaceRepositoryOptions } from '../common/workspaceRepositoryCliOptions';
17
+ import { normalizeProjectCliOptions } from '../common/projectCliOptions';
17
18
  import { handleActionErrors } from '../common/handleActionErrors';
18
19
  import { $ensureHarnessInstallations } from '../common/harness/$ensureHarnessInstallations';
19
20
  import { getHarnessDefinition } from '../common/harness/HarnessDefinition';
@@ -88,7 +89,7 @@ export function $initializeCoderInitCommand(program: Program): $side_effect {
88
89
  - Adds missing local Lawyer and Copywriter TEAM references to Developer and Planner, preserving existing content
89
90
  - Helpers advise on relevant tasks; declaring TEAM does not run them for every task
90
91
  - Reports created, augmented, unchanged and unresolved Books; invalid or conflicting files are left untouched
91
- - Other referenced files, like coder:run context, are created with their newly added scripts
92
+ - Missing AGENTS.md is initialized independently of scripts; existing content is preserved
92
93
 
93
94
  Ensures required coding-agent environment variables in .env:
94
95
  - CODING_AGENT_GIT_NAME
@@ -112,9 +113,14 @@ export function $initializeCoderInitCommand(program: Program): $side_effect {
112
113
 
113
114
  command.action(
114
115
  handleActionErrors(async (cliOptions) => {
116
+ const projectOptions = normalizeProjectCliOptions(cliOptions);
115
117
  const gitSync = normalizeCoderGitSyncCliOptions(cliOptions as CoderGitSyncCliOptions);
116
118
  const questionsOptions = normalizeQuestionsCliOptions(cliOptions as QuestionsCliOptions);
117
- const workspace = await $preflightWorkspaceRepository({ policy: 'initialize', ...questionsOptions });
119
+ const workspace = await $preflightWorkspaceRepository({
120
+ ...projectOptions,
121
+ policy: 'initialize',
122
+ ...questionsOptions,
123
+ });
118
124
  const { projectPath } = workspace;
119
125
 
120
126
  const completedSteps = [`Git repository ${workspace.repositoryStatus}: ${workspace.repositoryRoot}`];
@@ -38,6 +38,12 @@ export type CoderInitializationSummary = {
38
38
  readonly addedPackageJsonScriptNames: ReadonlyArray<string>;
39
39
  readonly referencedArtifactStatuses: ReadonlyArray<EnsuredCoderReferencedArtifact>;
40
40
  readonly initializedEnvVariableNames: ReadonlyArray<string>;
41
+ /** Actual preserved or generated scope of the project check. */
42
+ readonly checkScriptSummary: string;
43
+ /** Manual steps needed for custom or conflicting legacy entries. */
44
+ readonly migrationInstructions: ReadonlyArray<string>;
45
+ /** Whether validation is configured rather than still requiring setup. */
46
+ readonly isCheckConfigured: boolean;
41
47
  };
42
48
 
43
49
  /**
@@ -85,10 +91,13 @@ export async function initializeCoderProjectConfiguration(
85
91
  )!;
86
92
  const { envFileStatus, initializedEnvVariableNames } = await completeStep('.env', ensureCoderEnvFile(projectPath));
87
93
  const gitignoreFileStatus = await completeStep('.gitignore', ensureCoderGitignoreFile(projectPath));
88
- const { status: packageJsonFileStatus, addedEntryKeys: addedPackageJsonScriptNames } = await completeStep(
89
- 'package.json',
90
- ensureCoderPackageJsonFile(projectPath),
91
- );
94
+ const {
95
+ status: packageJsonFileStatus,
96
+ addedEntryKeys: addedPackageJsonScriptNames,
97
+ checkScriptSummary,
98
+ migrationInstructions,
99
+ isCheckConfigured,
100
+ } = await completeStep('package.json', ensureCoderPackageJsonFile(projectPath));
92
101
  const vscodeSettingsFileStatus = await completeStep(
93
102
  '.vscode/settings.json',
94
103
  ensureCoderVscodeSettingsFile(projectPath),
@@ -119,6 +128,9 @@ export async function initializeCoderProjectConfiguration(
119
128
  addedPackageJsonScriptNames,
120
129
  referencedArtifactStatuses,
121
130
  initializedEnvVariableNames,
131
+ checkScriptSummary,
132
+ migrationInstructions,
133
+ isCheckConfigured,
122
134
  };
123
135
  }
124
136
 
@@ -5,6 +5,7 @@ import { spaceTrim } from 'spacetrim';
5
5
  import type { $side_effect } from '../../../utils/organization/$side_effect';
6
6
  import { $preflightWorkspaceRepository } from '../common/workspaceRepository';
7
7
  import { addWorkspaceRepositoryOptions } from '../common/workspaceRepositoryCliOptions';
8
+ import { normalizeProjectCliOptions } from '../common/projectCliOptions';
8
9
  import { handleActionErrors } from '../common/handleActionErrors';
9
10
  import { addPromptPriorityOptions } from '../common/promptPriorityCliOptions';
10
11
  import type { PromptRunnerSelectionCliOptions } from '../common/promptRunnerCliOptions';
@@ -46,6 +47,7 @@ export function $initializeCoderListCommand(program: Program): $side_effect {
46
47
 
47
48
  command.action(
48
49
  handleActionErrors(async (cliOptions) => {
50
+ const projectOptions = normalizeProjectCliOptions(cliOptions);
49
51
  const {
50
52
  agent,
51
53
  priority,
@@ -62,7 +64,10 @@ export function $initializeCoderListCommand(program: Program): $side_effect {
62
64
  cliOptions as PromptRunnerSelectionCliOptions,
63
65
  { isAgentRequired: false },
64
66
  );
65
- const workspace = await $preflightWorkspaceRepository({ policy: 'read-only' });
67
+ const workspace = await $preflightWorkspaceRepository({
68
+ ...projectOptions,
69
+ policy: 'read-only',
70
+ });
66
71
  const resolvedCoderAgentBook =
67
72
  agent === undefined
68
73
  ? undefined
@@ -28,6 +28,10 @@ type MergeStringRecordJsonFileOptions = {
28
28
  readonly fieldPath: string;
29
29
  readonly nextEntries: Readonly<Record<string, string>>;
30
30
  readonly ensureParentDirectoryPath?: string;
31
+ /** Optional narrowly scoped migration of existing entries before missing defaults are added. */
32
+ readonly transformExistingEntries?: (
33
+ entries: Readonly<Record<string, string>>,
34
+ ) => Readonly<Record<string, string>> | Promise<Readonly<Record<string, string>>>;
31
35
  };
32
36
 
33
37
  /**
@@ -60,8 +64,8 @@ const DEFAULT_JSON_FILE_NEWLINE = '\n';
60
64
  /**
61
65
  * Ensures one JSON object field contains the provided string-record entries.
62
66
  *
63
- * Entries which the project already defines are **never** overridden - only missing keys are added,
64
- * so hand-tuned scripts and settings survive every repeated `ptbk coder init`.
67
+ * Entries are additive unless a caller explicitly supplies a scoped migration callback.
68
+ * Hand-tuned scripts and settings survive repeated `ptbk coder init`; migrations recognize generated entries only.
65
69
  *
66
70
  * @private function of `initializeCoderProjectConfiguration`
67
71
  */
@@ -71,6 +75,7 @@ export async function mergeStringRecordJsonFile({
71
75
  fieldPath,
72
76
  nextEntries,
73
77
  ensureParentDirectoryPath,
78
+ transformExistingEntries,
74
79
  }: MergeStringRecordJsonFileOptions): Promise<MergedStringRecordJsonFile> {
75
80
  if (ensureParentDirectoryPath) {
76
81
  await mkdir(join(projectPath, ensureParentDirectoryPath), { recursive: true });
@@ -83,7 +88,7 @@ export async function mergeStringRecordJsonFile({
83
88
  const existingEntries = getStringRecordOrDefault(jsonObject[fieldPath], relativeFilePath, fieldPath);
84
89
 
85
90
  const addedEntryKeys: Array<string> = [];
86
- const mergedEntries = { ...existingEntries };
91
+ const mergedEntries = { ...((await transformExistingEntries?.(existingEntries)) ?? existingEntries) };
87
92
  for (const [entryKey, entryValue] of Object.entries(nextEntries)) {
88
93
  if (Object.prototype.hasOwnProperty.call(mergedEntries, entryKey)) {
89
94
  // Note: The project already defines this entry, keep its own value untouched
@@ -93,8 +98,13 @@ export async function mergeStringRecordJsonFile({
93
98
  mergedEntries[entryKey] = entryValue;
94
99
  addedEntryKeys.push(entryKey);
95
100
  }
101
+ for (const entryKey of Object.keys(mergedEntries)) {
102
+ if (!Object.prototype.hasOwnProperty.call(existingEntries, entryKey) && !addedEntryKeys.includes(entryKey)) {
103
+ addedEntryKeys.push(entryKey);
104
+ }
105
+ }
96
106
 
97
- const hasChanges = fileContent === undefined || addedEntryKeys.length > 0;
107
+ const hasChanges = fileContent === undefined || JSON.stringify(mergedEntries) !== JSON.stringify(existingEntries);
98
108
  if (!hasChanges) {
99
109
  return { status: 'unchanged', addedEntryKeys };
100
110
  }