@promptbook/cli 0.114.0-2 → 0.114.0-3
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.
- package/apps/agents-server/next.config.ts +11 -0
- package/apps/agents-server/src/app/layout.tsx +6 -0
- package/esm/index.es.js +677 -199
- package/esm/index.es.js.map +1 -1
- package/esm/scripts/run-codex-prompts/common/runGoScript/scriptExecutionLog.d.ts +7 -0
- package/esm/scripts/run-codex-prompts/git/coderGitSync.d.ts +43 -0
- package/esm/scripts/run-codex-prompts/git/commitChanges.d.ts +3 -0
- package/esm/scripts/run-codex-prompts/main/resolvePromptRunner.d.ts +8 -1
- package/esm/scripts/run-codex-prompts/ping/buildCoderPingPrompt.d.ts +20 -0
- package/esm/scripts/run-codex-prompts/ping/extractCoderPingAnswer.d.ts +9 -0
- package/esm/scripts/run-codex-prompts/ping/pingCoderHarness.d.ts +23 -0
- package/esm/scripts/run-codex-prompts/ping/printCoderPingResult.d.ts +5 -0
- package/esm/scripts/verify-prompts/verify-prompts.d.ts +5 -0
- package/esm/src/book-components/Chat/utils/$provideServerDomWindow.d.ts +11 -0
- package/esm/src/cli/cli-commands/agents-server/buildAgentsServer/createAgentsServerRuntimeEnvironment.d.ts +7 -0
- package/esm/src/cli/cli-commands/coder/ping.d.ts +2 -5
- package/esm/src/cli/cli-commands/coder.d.ts +1 -1
- package/esm/src/cli/cli-commands/common/coderGitSyncCliOptions.d.ts +34 -0
- package/esm/src/cli/cli-commands/common/coderGitSyncCliOptions.test.d.ts +1 -0
- package/esm/src/cli/common/loadPromptsModule.d.ts +16 -0
- package/esm/src/llm-providers/anthropic-claude/AnthropicClaudeExecutionTools.d.ts +1 -1
- package/esm/src/llm-providers/azure-openai/AzureOpenAiExecutionTools.d.ts +1 -1
- package/esm/src/llm-providers/openai/OpenAiAgentKitExecutionTools.d.ts +1 -1
- package/esm/src/llm-providers/openai/OpenAiAgentKitExecutionToolsToolBuilder.d.ts +1 -1
- package/esm/src/llm-providers/openai/OpenAiAssistantExecutionToolsStreamRunner.d.ts +1 -1
- package/esm/src/llm-providers/openai/OpenAiAssistantExecutionToolsToolRunner.d.ts +1 -1
- package/esm/src/llm-providers/openai/OpenAiCompatibleNonChatPromptCaller.d.ts +1 -1
- package/esm/src/llm-providers/openai/OpenAiCompatibleRequestManager.d.ts +1 -1
- package/esm/src/llm-providers/openai/utils/callOpenAiCompatibleChatModel.d.ts +1 -1
- package/esm/src/llm-providers/openai/utils/loadOpenAiAgentsModule.d.ts +203 -0
- package/esm/src/llm-providers/openai/utils/uploadFilesToOpenAi.d.ts +1 -1
- package/esm/src/utils/misc/createLazyModuleLoader.d.ts +14 -0
- package/esm/src/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/book-3.0/LiteAgent.ts +11 -9
- package/src/book-components/Chat/utils/$provideServerDomWindow.ts +77 -0
- package/src/book-components/Chat/utils/renderMarkdown.ts +2 -23
- package/src/cli/cli-commands/agents-server/buildAgentsServer/createAgentsServerRuntimeEnvironment.ts +14 -0
- package/src/cli/cli-commands/agents-server/buildAgentsServer/ensureAgentsServerBuild.ts +1 -0
- package/src/cli/cli-commands/coder/add.ts +40 -12
- package/src/cli/cli-commands/coder/generate-boilerplates.ts +31 -5
- package/src/cli/cli-commands/coder/init.ts +27 -1
- package/src/cli/cli-commands/coder/ping.ts +23 -45
- package/src/cli/cli-commands/coder/verify.ts +27 -12
- package/src/cli/cli-commands/coder.ts +3 -3
- package/src/cli/cli-commands/common/coderGitSyncCliOptions.ts +78 -0
- package/src/cli/cli-commands/run/prepareRunCommandResources.ts +2 -1
- package/src/cli/cli-commands/run/resolveRunInputParameters.ts +2 -1
- package/src/cli/cli-commands/runInteractiveChatbot.ts +2 -1
- package/src/cli/common/$provideLlmToolsForCli.ts +2 -1
- package/src/cli/common/loadPromptsModule.ts +13 -0
- package/src/conversion/archive/loadArchive.ts +2 -1
- package/src/conversion/archive/loadJsZipModule.ts +10 -0
- package/src/conversion/archive/saveArchive.ts +2 -1
- package/src/llm-providers/anthropic-claude/AnthropicClaudeExecutionTools.ts +13 -1
- package/src/llm-providers/azure-openai/AzureOpenAiExecutionTools.ts +13 -1
- package/src/llm-providers/openai/OpenAiAgentKitExecutionTools.ts +6 -2
- package/src/llm-providers/openai/OpenAiAgentKitExecutionToolsToolBuilder.ts +7 -5
- package/src/llm-providers/openai/OpenAiAssistantExecutionTools.ts +1 -1
- package/src/llm-providers/openai/OpenAiAssistantExecutionToolsStreamRunner.ts +1 -1
- package/src/llm-providers/openai/OpenAiAssistantExecutionToolsToolRunner.ts +1 -1
- package/src/llm-providers/openai/OpenAiCompatibleNonChatPromptCaller.ts +1 -1
- package/src/llm-providers/openai/OpenAiCompatibleRequestManager.ts +12 -1
- package/src/llm-providers/openai/utils/callOpenAiCompatibleChatModel.ts +1 -1
- package/src/llm-providers/openai/utils/loadOpenAiAgentsModule.ts +11 -0
- package/src/llm-providers/openai/utils/uploadFilesToOpenAi.ts +1 -1
- package/src/other/templates/getTemplatesPipelineCollection.ts +678 -967
- package/src/remote-server/createRemoteClient.ts +12 -1
- package/src/scrapers/website/WebsiteScraper.ts +22 -2
- package/src/utils/misc/createLazyModuleLoader.ts +27 -0
- package/src/version.ts +2 -2
- package/src/versions.txt +1 -0
- package/umd/index.umd.js +750 -266
- package/umd/index.umd.js.map +1 -1
- package/umd/scripts/run-codex-prompts/common/runGoScript/scriptExecutionLog.d.ts +7 -0
- package/umd/scripts/run-codex-prompts/git/coderGitSync.d.ts +43 -0
- package/umd/scripts/run-codex-prompts/git/commitChanges.d.ts +3 -0
- package/umd/scripts/run-codex-prompts/main/resolvePromptRunner.d.ts +8 -1
- package/umd/scripts/run-codex-prompts/ping/buildCoderPingPrompt.d.ts +20 -0
- package/umd/scripts/run-codex-prompts/ping/extractCoderPingAnswer.d.ts +9 -0
- package/umd/scripts/run-codex-prompts/ping/pingCoderHarness.d.ts +23 -0
- package/umd/scripts/run-codex-prompts/ping/printCoderPingResult.d.ts +5 -0
- package/umd/scripts/verify-prompts/verify-prompts.d.ts +5 -0
- package/umd/src/book-components/Chat/utils/$provideServerDomWindow.d.ts +11 -0
- package/umd/src/cli/cli-commands/agents-server/buildAgentsServer/createAgentsServerRuntimeEnvironment.d.ts +7 -0
- package/umd/src/cli/cli-commands/coder/ping.d.ts +2 -5
- package/umd/src/cli/cli-commands/coder.d.ts +1 -1
- package/umd/src/cli/cli-commands/common/coderGitSyncCliOptions.d.ts +34 -0
- package/umd/src/cli/cli-commands/common/coderGitSyncCliOptions.test.d.ts +1 -0
- package/umd/src/cli/common/loadPromptsModule.d.ts +16 -0
- package/umd/src/llm-providers/anthropic-claude/AnthropicClaudeExecutionTools.d.ts +1 -1
- package/umd/src/llm-providers/azure-openai/AzureOpenAiExecutionTools.d.ts +1 -1
- package/umd/src/llm-providers/openai/OpenAiAgentKitExecutionTools.d.ts +1 -1
- package/umd/src/llm-providers/openai/OpenAiAgentKitExecutionToolsToolBuilder.d.ts +1 -1
- package/umd/src/llm-providers/openai/OpenAiAssistantExecutionToolsStreamRunner.d.ts +1 -1
- package/umd/src/llm-providers/openai/OpenAiAssistantExecutionToolsToolRunner.d.ts +1 -1
- package/umd/src/llm-providers/openai/OpenAiCompatibleNonChatPromptCaller.d.ts +1 -1
- package/umd/src/llm-providers/openai/OpenAiCompatibleRequestManager.d.ts +1 -1
- package/umd/src/llm-providers/openai/utils/callOpenAiCompatibleChatModel.d.ts +1 -1
- package/umd/src/llm-providers/openai/utils/loadOpenAiAgentsModule.d.ts +203 -0
- package/umd/src/llm-providers/openai/utils/uploadFilesToOpenAi.d.ts +1 -1
- package/umd/src/utils/misc/createLazyModuleLoader.d.ts +14 -0
- package/umd/src/version.d.ts +1 -1
- package/esm/scripts/run-agent-chat/runCoderPing.d.ts +0 -48
- package/umd/scripts/run-agent-chat/runCoderPing.d.ts +0 -48
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Agent as AgentFromKit,
|
|
3
|
-
fileSearchTool,
|
|
4
|
-
run,
|
|
5
|
-
setDefaultOpenAIClient,
|
|
6
|
-
setDefaultOpenAIKey,
|
|
7
|
-
} from '@openai/agents';
|
|
1
|
+
import type { Agent as AgentFromKit } from '@openai/agents';
|
|
8
2
|
import { readFile } from 'fs/promises';
|
|
9
3
|
import { basename, resolve } from 'path';
|
|
10
4
|
import { fileURLToPath } from 'url';
|
|
@@ -17,6 +11,7 @@ import { computeOpenAiUsage } from '../llm-providers/openai/computeOpenAiUsage';
|
|
|
17
11
|
import { OPENAI_MODELS } from '../llm-providers/openai/openai-models';
|
|
18
12
|
import { OpenAiAgentKitExecutionToolsToolBuilder } from '../llm-providers/openai/OpenAiAgentKitExecutionToolsToolBuilder';
|
|
19
13
|
import { OpenAiVectorStoreHandler } from '../llm-providers/openai/OpenAiVectorStoreHandler';
|
|
14
|
+
import { loadOpenAiAgentsModule } from '../llm-providers/openai/utils/loadOpenAiAgentsModule';
|
|
20
15
|
import { $provideScriptingForNode } from '../scrapers/_common/register/$provideScriptingForNode';
|
|
21
16
|
import type { string_knowledge_source_link } from '../types/string_knowledge_source_content';
|
|
22
17
|
import type { string_markdown, string_markdown_text } from '../types/string_markdown';
|
|
@@ -112,6 +107,7 @@ export class LiteAgent {
|
|
|
112
107
|
const preparedAgent = await this.prepareAgent();
|
|
113
108
|
preparedAgent.toolBuilder?.clearRunState();
|
|
114
109
|
|
|
110
|
+
const { run } = await loadOpenAiAgentsModule();
|
|
115
111
|
const result = await run(
|
|
116
112
|
preparedAgent.agent,
|
|
117
113
|
createLiteAgentPromptText(normalizedMessage, options.context, preparedAgent.modelRequirements.promptSuffix),
|
|
@@ -165,6 +161,12 @@ export class LiteAgent {
|
|
|
165
161
|
userId: this.options.userId,
|
|
166
162
|
});
|
|
167
163
|
const client = await runtime.getClient();
|
|
164
|
+
const {
|
|
165
|
+
Agent: AgentFromKit,
|
|
166
|
+
fileSearchTool,
|
|
167
|
+
setDefaultOpenAIClient,
|
|
168
|
+
setDefaultOpenAIKey,
|
|
169
|
+
} = await loadOpenAiAgentsModule();
|
|
168
170
|
|
|
169
171
|
setDefaultOpenAIClient(client as TODO_any);
|
|
170
172
|
|
|
@@ -190,9 +192,9 @@ export class LiteAgent {
|
|
|
190
192
|
})
|
|
191
193
|
: null;
|
|
192
194
|
const agentTools =
|
|
193
|
-
toolBuilder?.buildAgentKitTools({
|
|
195
|
+
(await toolBuilder?.buildAgentKitTools({
|
|
194
196
|
tools: modelRequirements.tools ? [...modelRequirements.tools] : undefined,
|
|
195
|
-
}) || [];
|
|
197
|
+
})) || [];
|
|
196
198
|
const normalizedKnowledgeSources = await normalizeLiteAgentKnowledgeSources(
|
|
197
199
|
modelRequirements.knowledgeSources || [],
|
|
198
200
|
resolvedSource.sourceDirectoryPath,
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { WindowLike as DomWindowLike } from 'dompurify';
|
|
2
|
+
import { spaceTrim } from 'spacetrim';
|
|
3
|
+
import { assertsError } from '../../../errors/assertsError';
|
|
4
|
+
import { EnvironmentMismatchError } from '../../../errors/EnvironmentMismatchError';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Shared DOM window reused by every render which happens outside of a browser
|
|
8
|
+
*
|
|
9
|
+
* @private utility of `$provideServerDomWindow`
|
|
10
|
+
*/
|
|
11
|
+
let serverDomWindow: DomWindowLike | null = null;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Provides a DOM `window` for runtimes which do not have one, for example Node.js during server-side rendering
|
|
15
|
+
*
|
|
16
|
+
* Note: `$` is used to indicate that this function is not a pure function - it creates and caches one shared DOM window
|
|
17
|
+
*
|
|
18
|
+
* @returns DOM window which can back a DOM-dependent library like `DOMPurify`
|
|
19
|
+
* @throws {EnvironmentMismatchError} When the current runtime can not create a DOM window
|
|
20
|
+
* @private utility of `renderMarkdown`
|
|
21
|
+
*/
|
|
22
|
+
export function $provideServerDomWindow(): DomWindowLike {
|
|
23
|
+
if (serverDomWindow === null) {
|
|
24
|
+
const { JSDOM } = requireServerDomLibrary();
|
|
25
|
+
serverDomWindow = new JSDOM('').window as unknown as DomWindowLike;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return serverDomWindow;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Loads `jsdom` which backs the DOM window outside of a browser
|
|
33
|
+
*
|
|
34
|
+
* Note: [😷] `jsdom` reaches for Node.js built-ins like `fs`, `child_process` and `net`, so browser bundlers must
|
|
35
|
+
* never follow the `require` below - even though it is unreachable in a browser, bundlers resolve it statically.
|
|
36
|
+
* They are told to replace `jsdom` with an empty module by the `browser` field of the generated package
|
|
37
|
+
* manifests, which is written by `applyBrowserUnsupportedDependencyStubs` during the package generation.
|
|
38
|
+
*
|
|
39
|
+
* @returns The `jsdom` library
|
|
40
|
+
* @throws {EnvironmentMismatchError} When `jsdom` can not be loaded in the current runtime
|
|
41
|
+
* @private utility of `$provideServerDomWindow`
|
|
42
|
+
*/
|
|
43
|
+
function requireServerDomLibrary(): typeof import('jsdom') {
|
|
44
|
+
let serverDomLibrary: Partial<typeof import('jsdom')> | null = null;
|
|
45
|
+
let serverDomLibraryError: Error | null = null;
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
// Note: [😷] Keep `'jsdom'` written as a literal, bundlers do not replace a computed module name
|
|
49
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
50
|
+
serverDomLibrary = require('jsdom') as typeof import('jsdom');
|
|
51
|
+
} catch (error) {
|
|
52
|
+
assertsError(error);
|
|
53
|
+
serverDomLibraryError = error;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (typeof serverDomLibrary?.JSDOM !== 'function') {
|
|
57
|
+
throw new EnvironmentMismatchError(
|
|
58
|
+
spaceTrim(
|
|
59
|
+
(block) => `
|
|
60
|
+
Markdown can not be rendered because there is no DOM in the current runtime.
|
|
61
|
+
|
|
62
|
+
Rendered markdown is sanitized through \`DOMPurify\`, which needs a DOM - the DOM of the browser
|
|
63
|
+
when the code runs in a browser and \`jsdom\` in every other runtime, for example during
|
|
64
|
+
server-side rendering in **Node.js**.
|
|
65
|
+
|
|
66
|
+
${block(
|
|
67
|
+
serverDomLibraryError === null
|
|
68
|
+
? 'The `jsdom` module was resolved but it does not export `JSDOM`, which happens when a bundler replaces `jsdom` with an empty module in a build which is neither a browser build nor a Node.js build.'
|
|
69
|
+
: `${serverDomLibraryError.name}: ${serverDomLibraryError.message}`,
|
|
70
|
+
)}
|
|
71
|
+
`,
|
|
72
|
+
),
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return serverDomLibrary as typeof import('jsdom');
|
|
77
|
+
}
|
|
@@ -9,6 +9,7 @@ import type { Converter as ShowdownConverter } from 'showdown';
|
|
|
9
9
|
import showdown from 'showdown';
|
|
10
10
|
import type { string_html, string_markdown } from '../../../types/string_markdown';
|
|
11
11
|
import { TODO_USE } from '../../../utils/organization/TODO_USE';
|
|
12
|
+
import { $provideServerDomWindow } from './$provideServerDomWindow';
|
|
12
13
|
import { createCitationMarkerRegex, parseCitationMarker } from './parseCitationMarker';
|
|
13
14
|
|
|
14
15
|
/**
|
|
@@ -218,13 +219,6 @@ let browserMarkdownSanitizer: DomPurifyInstance | null = null;
|
|
|
218
219
|
*/
|
|
219
220
|
let serverMarkdownSanitizer: DomPurifyInstance | null = null;
|
|
220
221
|
|
|
221
|
-
/**
|
|
222
|
-
* Shared JSDOM window backing the server-side DOMPurify instance.
|
|
223
|
-
*
|
|
224
|
-
* @private utility of `renderMarkdown`
|
|
225
|
-
*/
|
|
226
|
-
let serverMarkdownSanitizerWindow: DomPurifyWindowLike | null = null;
|
|
227
|
-
|
|
228
222
|
/**
|
|
229
223
|
* Pattern matching CODE FENCE.
|
|
230
224
|
*/
|
|
@@ -546,21 +540,6 @@ function createMarkdownSanitizer(sanitizerWindow: DomPurifyWindowLike): DomPurif
|
|
|
546
540
|
return markdownSanitizer;
|
|
547
541
|
}
|
|
548
542
|
|
|
549
|
-
/**
|
|
550
|
-
* Lazily creates the JSDOM window used by server-side markdown sanitization.
|
|
551
|
-
*
|
|
552
|
-
* @private utility of `renderMarkdown`
|
|
553
|
-
*/
|
|
554
|
-
function getServerMarkdownSanitizerWindow(): DomPurifyWindowLike {
|
|
555
|
-
if (!serverMarkdownSanitizerWindow) {
|
|
556
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
557
|
-
const { JSDOM } = require('jsdom') as typeof import('jsdom');
|
|
558
|
-
serverMarkdownSanitizerWindow = new JSDOM('').window as unknown as DomPurifyWindowLike;
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
return serverMarkdownSanitizerWindow;
|
|
562
|
-
}
|
|
563
|
-
|
|
564
543
|
/**
|
|
565
544
|
* Returns the shared DOMPurify instance appropriate for the current runtime.
|
|
566
545
|
*
|
|
@@ -573,7 +552,7 @@ function getMarkdownSanitizer(): DomPurifyInstance {
|
|
|
573
552
|
return browserMarkdownSanitizer;
|
|
574
553
|
}
|
|
575
554
|
|
|
576
|
-
serverMarkdownSanitizer ??= createMarkdownSanitizer(
|
|
555
|
+
serverMarkdownSanitizer ??= createMarkdownSanitizer($provideServerDomWindow());
|
|
577
556
|
|
|
578
557
|
return serverMarkdownSanitizer;
|
|
579
558
|
}
|
package/src/cli/cli-commands/agents-server/buildAgentsServer/createAgentsServerRuntimeEnvironment.ts
CHANGED
|
@@ -22,6 +22,14 @@ export const PTBK_AGENTS_SERVER_BUILD_WORKER_COUNT_ENV = 'PTBK_AGENTS_SERVER_BUI
|
|
|
22
22
|
*/
|
|
23
23
|
export const PTBK_AGENTS_SERVER_IGNORE_NEXT_VALIDATION_ENV = 'PTBK_AGENTS_SERVER_IGNORE_NEXT_VALIDATION';
|
|
24
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Environment variable that disables throwaway webpack filesystem caches for CLI-owned production builds.
|
|
27
|
+
*
|
|
28
|
+
* @private internal constant of `buildAgentsServer`
|
|
29
|
+
*/
|
|
30
|
+
export const PTBK_AGENTS_SERVER_DISABLE_WEBPACK_FILESYSTEM_CACHE_ENV =
|
|
31
|
+
'PTBK_AGENTS_SERVER_DISABLE_WEBPACK_FILESYSTEM_CACHE';
|
|
32
|
+
|
|
25
33
|
/**
|
|
26
34
|
* Conservative Next.js build worker count used by CLI-owned Agents Server production builds.
|
|
27
35
|
*
|
|
@@ -41,6 +49,7 @@ export function createAgentsServerRuntimeEnvironment(
|
|
|
41
49
|
nodeModulesPath: string,
|
|
42
50
|
options: {
|
|
43
51
|
readonly isNextValidationIgnored?: boolean;
|
|
52
|
+
readonly isWebpackFilesystemCacheDisabled?: boolean;
|
|
44
53
|
} = {},
|
|
45
54
|
): NodeJS.ProcessEnv {
|
|
46
55
|
return {
|
|
@@ -54,6 +63,11 @@ export function createAgentsServerRuntimeEnvironment(
|
|
|
54
63
|
[PTBK_AGENTS_SERVER_IGNORE_NEXT_VALIDATION_ENV]: 'true',
|
|
55
64
|
}
|
|
56
65
|
: {}),
|
|
66
|
+
...(options.isWebpackFilesystemCacheDisabled
|
|
67
|
+
? {
|
|
68
|
+
[PTBK_AGENTS_SERVER_DISABLE_WEBPACK_FILESYSTEM_CACHE_ENV]: 'true',
|
|
69
|
+
}
|
|
70
|
+
: {}),
|
|
57
71
|
};
|
|
58
72
|
}
|
|
59
73
|
|
|
@@ -30,6 +30,7 @@ export async function ensureAgentsServerBuild(
|
|
|
30
30
|
});
|
|
31
31
|
const buildEnvironment = createAgentsServerRuntimeEnvironment(environment, preparedRuntime.nodeModulesPath, {
|
|
32
32
|
isNextValidationIgnored: preparedRuntime.isAppPathMaterialized,
|
|
33
|
+
isWebpackFilesystemCacheDisabled: true,
|
|
33
34
|
});
|
|
34
35
|
|
|
35
36
|
if (
|
|
@@ -4,12 +4,18 @@ import type {
|
|
|
4
4
|
} from 'commander';
|
|
5
5
|
import { mkdirSync, writeFileSync } from 'fs';
|
|
6
6
|
import { join } from 'path';
|
|
7
|
-
import prompts from 'prompts';
|
|
8
7
|
import { spaceTrim } from 'spacetrim';
|
|
9
8
|
import { NotAllowed } from '../../../errors/NotAllowed';
|
|
10
9
|
import { ParseError } from '../../../errors/ParseError';
|
|
11
10
|
import { normalizeToKebabCase } from '../../../utils/normalization/normalize-to-kebab-case';
|
|
12
11
|
import type { $side_effect } from '../../../utils/organization/$side_effect';
|
|
12
|
+
import { loadPromptsModule } from '../../common/loadPromptsModule';
|
|
13
|
+
import type { CoderGitSyncCliOptions } from '../common/coderGitSyncCliOptions';
|
|
14
|
+
import {
|
|
15
|
+
addCoderGitSyncOptions,
|
|
16
|
+
CODER_GIT_SYNC_DESCRIPTION,
|
|
17
|
+
normalizeCoderGitSyncCliOptions,
|
|
18
|
+
} from '../common/coderGitSyncCliOptions';
|
|
13
19
|
import { handleActionErrors } from '../common/handleActionErrors';
|
|
14
20
|
import {
|
|
15
21
|
buildCoderPromptSection,
|
|
@@ -49,15 +55,19 @@ const FALLBACK_PROMPT_SLUG = 'prompt';
|
|
|
49
55
|
export function $initializeCoderAddCommand(program: Program): $side_effect {
|
|
50
56
|
const command = program.command('add');
|
|
51
57
|
command.description(
|
|
52
|
-
spaceTrim(
|
|
53
|
-
|
|
58
|
+
spaceTrim(
|
|
59
|
+
(block) => `
|
|
60
|
+
Add one ready-to-run prompt file to the queue
|
|
54
61
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
62
|
+
Provide the description as an argument, pipe it through stdin, or run without arguments to type it interactively:
|
|
63
|
+
- \`ptbk coder add "some new feature"\`
|
|
64
|
+
- \`ptbk coder add --priority 1 "some new feature"\`
|
|
65
|
+
- \`ptbk coder add <<EOF ... EOF\`
|
|
66
|
+
- \`ptbk coder add\`
|
|
67
|
+
|
|
68
|
+
${block(CODER_GIT_SYNC_DESCRIPTION)}
|
|
69
|
+
`,
|
|
70
|
+
),
|
|
61
71
|
);
|
|
62
72
|
|
|
63
73
|
command.argument('[description]', 'Plain-language description of the feature or task to implement');
|
|
@@ -77,22 +87,39 @@ export function $initializeCoderAddCommand(program: Program): $side_effect {
|
|
|
77
87
|
.join(', ')}) or a markdown file path relative to the current project root.
|
|
78
88
|
`),
|
|
79
89
|
);
|
|
90
|
+
addCoderGitSyncOptions(command);
|
|
80
91
|
|
|
81
92
|
command.action(
|
|
82
93
|
handleActionErrors(async (descriptionArgument: string | undefined, cliOptions) => {
|
|
83
94
|
const { priority, template: templateOption } = cliOptions as {
|
|
84
95
|
readonly priority: number;
|
|
85
96
|
readonly template?: string;
|
|
86
|
-
};
|
|
97
|
+
} & CoderGitSyncCliOptions;
|
|
98
|
+
|
|
99
|
+
const gitSync = normalizeCoderGitSyncCliOptions(cliOptions as CoderGitSyncCliOptions);
|
|
100
|
+
const projectPath = process.cwd();
|
|
87
101
|
|
|
88
102
|
const description = await resolveCoderPromptDescription(descriptionArgument);
|
|
89
103
|
|
|
90
|
-
|
|
91
|
-
|
|
104
|
+
// Note: Import the git synchronization dynamically to keep the CLI fast for runs without `--commit`
|
|
105
|
+
const { $commitCoderChanges, $pullCoderChanges } = await import(
|
|
106
|
+
'../../../../scripts/run-codex-prompts/git/coderGitSync'
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
await $pullCoderChanges({ gitSync, projectPath });
|
|
110
|
+
|
|
111
|
+
const { /* filePath,*/ emojiTag } = await addCoderPrompt({
|
|
112
|
+
projectPath,
|
|
92
113
|
description,
|
|
93
114
|
priority,
|
|
94
115
|
templateOption,
|
|
95
116
|
});
|
|
117
|
+
|
|
118
|
+
await $commitCoderChanges({
|
|
119
|
+
gitSync,
|
|
120
|
+
projectPath,
|
|
121
|
+
commitMessage: `${emojiTag} Add prompt`,
|
|
122
|
+
});
|
|
96
123
|
}),
|
|
97
124
|
);
|
|
98
125
|
}
|
|
@@ -216,6 +243,7 @@ async function resolveCoderPromptDescription(descriptionArgument: string | undef
|
|
|
216
243
|
return standardInputDescription;
|
|
217
244
|
}
|
|
218
245
|
|
|
246
|
+
const { default: prompts } = await loadPromptsModule();
|
|
219
247
|
const response = await prompts({
|
|
220
248
|
type: 'text',
|
|
221
249
|
name: 'description',
|
|
@@ -6,6 +6,12 @@ import { mkdirSync, writeFileSync } from 'fs';
|
|
|
6
6
|
import { join } from 'path';
|
|
7
7
|
import { spaceTrim } from 'spacetrim';
|
|
8
8
|
import type { $side_effect } from '../../../utils/organization/$side_effect';
|
|
9
|
+
import type { CoderGitSyncCliOptions } from '../common/coderGitSyncCliOptions';
|
|
10
|
+
import {
|
|
11
|
+
addCoderGitSyncOptions,
|
|
12
|
+
CODER_GIT_SYNC_DESCRIPTION,
|
|
13
|
+
normalizeCoderGitSyncCliOptions,
|
|
14
|
+
} from '../common/coderGitSyncCliOptions';
|
|
9
15
|
import { handleActionErrors } from '../common/handleActionErrors';
|
|
10
16
|
import {
|
|
11
17
|
buildCoderPromptSection,
|
|
@@ -24,9 +30,13 @@ import {
|
|
|
24
30
|
export function $initializeCoderGenerateBoilerplatesCommand(program: Program): $side_effect {
|
|
25
31
|
const command = program.command('generate-boilerplates');
|
|
26
32
|
command.description(
|
|
27
|
-
spaceTrim(
|
|
28
|
-
|
|
29
|
-
|
|
33
|
+
spaceTrim(
|
|
34
|
+
(block) => `
|
|
35
|
+
Generate prompt boilerplate files with unique emoji tags
|
|
36
|
+
|
|
37
|
+
${block(CODER_GIT_SYNC_DESCRIPTION)}
|
|
38
|
+
`,
|
|
39
|
+
),
|
|
30
40
|
);
|
|
31
41
|
|
|
32
42
|
command.option('--count <count>', `Number of prompt boilerplate files to generate`, '5');
|
|
@@ -40,22 +50,38 @@ export function $initializeCoderGenerateBoilerplatesCommand(program: Program): $
|
|
|
40
50
|
.join(', ')}) or a markdown file path relative to the current project root.
|
|
41
51
|
`),
|
|
42
52
|
);
|
|
53
|
+
addCoderGitSyncOptions(command);
|
|
43
54
|
|
|
44
55
|
command.action(
|
|
45
56
|
handleActionErrors(async (cliOptions) => {
|
|
46
57
|
const { count: countOption, template: templateOption } = cliOptions as {
|
|
47
58
|
readonly count: string;
|
|
48
59
|
readonly template?: string;
|
|
49
|
-
};
|
|
60
|
+
} & CoderGitSyncCliOptions;
|
|
50
61
|
|
|
51
62
|
const filesCount = parseFilesCount(countOption);
|
|
63
|
+
const gitSync = normalizeCoderGitSyncCliOptions(cliOptions as CoderGitSyncCliOptions);
|
|
64
|
+
const projectPath = process.cwd();
|
|
65
|
+
|
|
66
|
+
// Note: Import the git synchronization dynamically to keep the CLI fast for runs without `--commit`
|
|
67
|
+
const { $commitCoderChanges, $pullCoderChanges } = await import(
|
|
68
|
+
'../../../../scripts/run-codex-prompts/git/coderGitSync'
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
await $pullCoderChanges({ gitSync, projectPath });
|
|
52
72
|
|
|
53
73
|
await generatePromptBoilerplate({
|
|
54
|
-
projectPath
|
|
74
|
+
projectPath,
|
|
55
75
|
filesCount,
|
|
56
76
|
templateOption,
|
|
57
77
|
});
|
|
58
78
|
|
|
79
|
+
await $commitCoderChanges({
|
|
80
|
+
gitSync,
|
|
81
|
+
projectPath,
|
|
82
|
+
commitMessage: `Prompts ${filesCount}x`,
|
|
83
|
+
});
|
|
84
|
+
|
|
59
85
|
return process.exit(0);
|
|
60
86
|
}),
|
|
61
87
|
);
|
|
@@ -3,6 +3,12 @@ import type {
|
|
|
3
3
|
} from 'commander';
|
|
4
4
|
import { spaceTrim } from 'spacetrim';
|
|
5
5
|
import type { $side_effect } from '../../../utils/organization/$side_effect';
|
|
6
|
+
import type { CoderGitSyncCliOptions } from '../common/coderGitSyncCliOptions';
|
|
7
|
+
import {
|
|
8
|
+
addCoderGitSyncOptions,
|
|
9
|
+
CODER_GIT_SYNC_DESCRIPTION,
|
|
10
|
+
normalizeCoderGitSyncCliOptions,
|
|
11
|
+
} from '../common/coderGitSyncCliOptions';
|
|
6
12
|
import { handleActionErrors } from '../common/handleActionErrors';
|
|
7
13
|
import { $ensureHarnessInstallations } from '../common/harness/$ensureHarnessInstallations';
|
|
8
14
|
import { getHarnessDefinition } from '../common/harness/HarnessDefinition';
|
|
@@ -61,16 +67,36 @@ export function $initializeCoderInitCommand(program: Program): $side_effect {
|
|
|
61
67
|
|
|
62
68
|
Checks that the coding harnesses are installed globally and up to date:
|
|
63
69
|
${block(listCheckedHarnessLabels())}
|
|
70
|
+
|
|
71
|
+
${block(CODER_GIT_SYNC_DESCRIPTION)}
|
|
64
72
|
`,
|
|
65
73
|
),
|
|
66
74
|
);
|
|
67
75
|
|
|
76
|
+
addCoderGitSyncOptions(command);
|
|
77
|
+
|
|
68
78
|
command.action(
|
|
69
|
-
handleActionErrors(async () => {
|
|
79
|
+
handleActionErrors(async (cliOptions) => {
|
|
80
|
+
const gitSync = normalizeCoderGitSyncCliOptions(cliOptions as CoderGitSyncCliOptions);
|
|
70
81
|
const projectPath = process.cwd();
|
|
82
|
+
|
|
83
|
+
// Note: Import the git synchronization dynamically to keep the CLI fast for runs without `--commit`
|
|
84
|
+
const { $commitCoderChanges, $pullCoderChanges } = await import(
|
|
85
|
+
'../../../../scripts/run-codex-prompts/git/coderGitSync'
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
await $pullCoderChanges({ gitSync, projectPath });
|
|
89
|
+
|
|
71
90
|
const summary = await initializeCoderProjectConfiguration(projectPath);
|
|
72
91
|
printInitializationSummary(summary);
|
|
73
92
|
await generatePromptBoilerplate({ projectPath, filesCount: 5 });
|
|
93
|
+
|
|
94
|
+
await $commitCoderChanges({
|
|
95
|
+
gitSync,
|
|
96
|
+
projectPath,
|
|
97
|
+
commitMessage: 'Initialize Promptbook Coder',
|
|
98
|
+
});
|
|
99
|
+
|
|
74
100
|
await $ensureHarnessInstallations(CODER_INIT_CHECKED_HARNESS_NAMES);
|
|
75
101
|
}),
|
|
76
102
|
);
|
|
@@ -2,10 +2,9 @@ import type {
|
|
|
2
2
|
Command as Program /* <- Note: [🔸] Using Program because Command is misleading name */,
|
|
3
3
|
} from 'commander';
|
|
4
4
|
import { spaceTrim } from 'spacetrim';
|
|
5
|
-
import { NotAllowed } from '../../../errors/NotAllowed';
|
|
6
5
|
import type { $side_effect } from '../../../utils/organization/$side_effect';
|
|
7
|
-
import { $ensureHarnessInstallations } from '../common/harness/$ensureHarnessInstallations';
|
|
8
6
|
import { handleActionErrors } from '../common/handleActionErrors';
|
|
7
|
+
import { $ensureHarnessInstallations } from '../common/harness/$ensureHarnessInstallations';
|
|
9
8
|
import type { PromptRunnerSelectionCliOptions } from '../common/promptRunnerCliOptions';
|
|
10
9
|
import {
|
|
11
10
|
addPromptRunnerRuntimeOptions,
|
|
@@ -15,12 +14,9 @@ import {
|
|
|
15
14
|
} from '../common/promptRunnerCliOptions';
|
|
16
15
|
|
|
17
16
|
/**
|
|
18
|
-
* Initializes `coder ping` command for Promptbook CLI utilities
|
|
19
|
-
*
|
|
20
|
-
* The command makes one small, isolated harness/model call, prints its result
|
|
21
|
-
* and reports how long the harness/model turn took.
|
|
17
|
+
* Initializes `coder ping` command for Promptbook CLI utilities
|
|
22
18
|
*
|
|
23
|
-
* Note: `$` is used to indicate that this function is not a pure function - it registers a command in the CLI
|
|
19
|
+
* Note: `$` is used to indicate that this function is not a pure function - it registers a command in the CLI
|
|
24
20
|
*
|
|
25
21
|
* @private internal function of `promptbookCli`
|
|
26
22
|
*/
|
|
@@ -28,15 +24,16 @@ export function $initializeCoderPingCommand(program: Program): $side_effect {
|
|
|
28
24
|
const command = program.command('ping');
|
|
29
25
|
command.description(
|
|
30
26
|
spaceTrim(`
|
|
31
|
-
|
|
27
|
+
Send one tiny dummy prompt to a harness and model to measure and warm them up
|
|
32
28
|
|
|
33
29
|
${PROMPT_RUNNER_DESCRIPTION}
|
|
34
30
|
|
|
35
31
|
Features:
|
|
36
|
-
-
|
|
37
|
-
-
|
|
38
|
-
-
|
|
39
|
-
-
|
|
32
|
+
- Verifies that the selected harness, model, thinking level and authentication really work
|
|
33
|
+
- Reports the answer of the harness, the response time and the reported usage
|
|
34
|
+
- Starts the hourly/weekly quota window before you need it, so it is already refreshing when you do
|
|
35
|
+
- Leaves the project exactly as it was — nothing is read, written, changed or committed
|
|
36
|
+
- Use --no-ui to stream the raw harness output instead of only the compact result
|
|
40
37
|
`),
|
|
41
38
|
);
|
|
42
39
|
|
|
@@ -45,50 +42,31 @@ export function $initializeCoderPingCommand(program: Program): $side_effect {
|
|
|
45
42
|
|
|
46
43
|
command.action(
|
|
47
44
|
handleActionErrors(async (cliOptions) => {
|
|
48
|
-
const
|
|
45
|
+
const runnerOptions = normalizePromptRunnerSelectionCliOptions(
|
|
49
46
|
cliOptions as PromptRunnerSelectionCliOptions,
|
|
50
47
|
{ isAgentRequired: true },
|
|
51
48
|
);
|
|
52
49
|
|
|
53
|
-
|
|
54
|
-
if (!agentName) {
|
|
55
|
-
throw new NotAllowed(
|
|
56
|
-
spaceTrim(`
|
|
57
|
-
A harness is required for \`ptbk coder ping\`.
|
|
50
|
+
await $ensureHarnessInstallations([runnerOptions.agentName]);
|
|
58
51
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
await $ensureHarnessInstallations([agentName]);
|
|
52
|
+
// Note: Import the ping dynamically to avoid loading heavy dependencies until needed
|
|
53
|
+
const { pingCoderHarness } = await import('../../../../scripts/run-codex-prompts/ping/pingCoderHarness');
|
|
54
|
+
const { printCoderPingResult } = await import(
|
|
55
|
+
'../../../../scripts/run-codex-prompts/ping/printCoderPingResult'
|
|
56
|
+
);
|
|
65
57
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
isUiDisabled: normalizedOptions.noUi,
|
|
73
|
-
isCreditsAllowed: normalizedOptions.allowCredits,
|
|
58
|
+
const result = await pingCoderHarness({
|
|
59
|
+
agentName: runnerOptions.agentName,
|
|
60
|
+
model: runnerOptions.model,
|
|
61
|
+
thinkingLevel: runnerOptions.thinkingLevel,
|
|
62
|
+
allowCredits: runnerOptions.allowCredits,
|
|
63
|
+
shouldPrintLiveOutput: runnerOptions.noUi,
|
|
74
64
|
});
|
|
75
65
|
|
|
76
|
-
printCoderPingResult(
|
|
66
|
+
printCoderPingResult(result);
|
|
77
67
|
}),
|
|
78
68
|
);
|
|
79
69
|
}
|
|
80
70
|
|
|
81
|
-
/**
|
|
82
|
-
* Prints the result and duration of one completed coder ping.
|
|
83
|
-
*/
|
|
84
|
-
function printCoderPingResult(pingResult: { readonly result: string; readonly elapsedTimeMs: number }): void {
|
|
85
|
-
console.info(
|
|
86
|
-
spaceTrim(`
|
|
87
|
-
Result: ${pingResult.result}
|
|
88
|
-
Time: ${pingResult.elapsedTimeMs.toFixed(0)} ms
|
|
89
|
-
`),
|
|
90
|
-
);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
71
|
// Note: [🟡] Code for CLI command [ping](src/cli/cli-commands/coder/ping.ts) should never be published outside of `@promptbook/cli`
|
|
94
72
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
@@ -4,6 +4,12 @@ import type {
|
|
|
4
4
|
} from 'commander';
|
|
5
5
|
import { spaceTrim } from 'spacetrim';
|
|
6
6
|
import type { $side_effect } from '../../../utils/organization/$side_effect';
|
|
7
|
+
import type { CoderGitSyncCliOptions } from '../common/coderGitSyncCliOptions';
|
|
8
|
+
import {
|
|
9
|
+
addCoderGitSyncOptions,
|
|
10
|
+
CODER_GIT_SYNC_DESCRIPTION,
|
|
11
|
+
normalizeCoderGitSyncCliOptions,
|
|
12
|
+
} from '../common/coderGitSyncCliOptions';
|
|
7
13
|
import { handleActionErrors } from '../common/handleActionErrors';
|
|
8
14
|
|
|
9
15
|
/**
|
|
@@ -16,17 +22,23 @@ import { handleActionErrors } from '../common/handleActionErrors';
|
|
|
16
22
|
export function $initializeCoderVerifyCommand(program: Program): $side_effect {
|
|
17
23
|
const command = program.command('verify');
|
|
18
24
|
command.description(
|
|
19
|
-
spaceTrim(
|
|
20
|
-
|
|
25
|
+
spaceTrim(
|
|
26
|
+
(block) => `
|
|
27
|
+
Interactive verification helper for completed prompts
|
|
21
28
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
Features:
|
|
30
|
+
- Displays list of prompt files with status counts
|
|
31
|
+
- Guides through verification of completed prompts marked [x]
|
|
32
|
+
- Archives verified prompt files to prompts/done/ directory
|
|
33
|
+
- Auto-appends repair prompts for incomplete work
|
|
34
|
+
- Processes files with all-done prompts first
|
|
35
|
+
- Supports ignoring matching prompt candidates for one verification run
|
|
36
|
+
|
|
37
|
+
${block(CODER_GIT_SYNC_DESCRIPTION)}
|
|
38
|
+
|
|
39
|
+
Note: The git synchronization is applied around each single verification, not once per run.
|
|
40
|
+
`,
|
|
41
|
+
),
|
|
30
42
|
);
|
|
31
43
|
|
|
32
44
|
command.option('--reverse', 'Process prompt files in reverse order', false);
|
|
@@ -36,19 +48,22 @@ export function $initializeCoderVerifyCommand(program: Program): $side_effect {
|
|
|
36
48
|
collectStringOption,
|
|
37
49
|
[],
|
|
38
50
|
);
|
|
51
|
+
addCoderGitSyncOptions(command);
|
|
39
52
|
|
|
40
53
|
command.action(
|
|
41
54
|
handleActionErrors(async (cliOptions) => {
|
|
42
55
|
const { reverse, ignore } = cliOptions as {
|
|
43
56
|
readonly reverse: boolean;
|
|
44
57
|
readonly ignore: ReadonlyArray<string>;
|
|
45
|
-
};
|
|
58
|
+
} & CoderGitSyncCliOptions;
|
|
59
|
+
|
|
60
|
+
const gitSync = normalizeCoderGitSyncCliOptions(cliOptions as CoderGitSyncCliOptions);
|
|
46
61
|
|
|
47
62
|
// Note: Import the main function dynamically to avoid loading heavy dependencies until needed
|
|
48
63
|
const { verifyPrompts } = await import('../../../../scripts/verify-prompts/verify-prompts');
|
|
49
64
|
|
|
50
65
|
try {
|
|
51
|
-
await verifyPrompts({ reverse, ignore });
|
|
66
|
+
await verifyPrompts({ reverse, ignore, gitSync });
|
|
52
67
|
} catch (error) {
|
|
53
68
|
console.error(colors.bgRed('Prompt verification failed:'), error);
|
|
54
69
|
return process.exit(1);
|
|
@@ -23,8 +23,8 @@ import { $initializeCoderVerifyCommand } from './coder/verify';
|
|
|
23
23
|
* - add: Add one ready-to-run prompt file to the queue
|
|
24
24
|
* - generate-boilerplates: Generate prompt boilerplate files
|
|
25
25
|
* - find-refactor-candidates: Find files that need refactoring
|
|
26
|
-
* - ping: Test one harness and model connection with a disposable task
|
|
27
26
|
* - run: Run coding prompts with AI agents
|
|
27
|
+
* - ping: Test one harness and model with a tiny dummy prompt
|
|
28
28
|
* - verify: Verify completed prompts
|
|
29
29
|
* - find-fresh-emoji-tags: Find unused emoji tags
|
|
30
30
|
*
|
|
@@ -44,8 +44,8 @@ export function $initializeCoderCommand(program: Program): $side_effect {
|
|
|
44
44
|
- generate-boilerplates: Generate prompt boilerplate files
|
|
45
45
|
- find-refactor-candidates: Find files that need refactoring
|
|
46
46
|
- find-unwritten: List prompt sections that still need to be authored
|
|
47
|
-
- ping: Test one harness and model connection with a disposable task
|
|
48
47
|
- run: Run coding prompts with AI agents
|
|
48
|
+
- ping: Test the connection, response time and quota of one harness and model
|
|
49
49
|
- server: Start a long-running coder server with a kanban web UI
|
|
50
50
|
- verify: Verify completed prompts
|
|
51
51
|
- find-fresh-emoji-tags: Find unused emoji tags
|
|
@@ -58,8 +58,8 @@ export function $initializeCoderCommand(program: Program): $side_effect {
|
|
|
58
58
|
$initializeCoderGenerateBoilerplatesCommand(coderCommand);
|
|
59
59
|
$initializeCoderFindRefactorCandidatesCommand(coderCommand);
|
|
60
60
|
$initializeCoderFindUnwrittenCommand(coderCommand);
|
|
61
|
-
$initializeCoderPingCommand(coderCommand);
|
|
62
61
|
$initializeCoderRunCommand(coderCommand);
|
|
62
|
+
$initializeCoderPingCommand(coderCommand);
|
|
63
63
|
$initializeCoderServerCommand(coderCommand);
|
|
64
64
|
$initializeCoderVerifyCommand(coderCommand);
|
|
65
65
|
$initializeCoderFindFreshEmojiTagCommand(coderCommand);
|