@promptbook/cli 0.112.0-46 → 0.112.0-47
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/README.md +16 -16
- package/esm/index.es.js +689 -250
- package/esm/index.es.js.map +1 -1
- package/esm/scripts/run-codex-prompts/common/waitForPause.d.ts +13 -1
- package/esm/scripts/run-codex-prompts/ui/buildCoderRunOctopusVisual.d.ts +13 -0
- package/esm/scripts/run-codex-prompts/ui/buildCoderRunUiFrame.d.ts +3 -1
- package/esm/scripts/run-codex-prompts/ui/coderRunUiRefresh.d.ts +8 -2
- package/esm/scripts/run-codex-prompts/ui/coderRunUiText.d.ts +36 -0
- package/esm/scripts/utils/emojiTags/scanEmojiTagUsage.d.ts +51 -0
- package/esm/src/avatars/AvatarOrImage.d.ts +45 -0
- package/esm/src/avatars/index.d.ts +1 -0
- package/esm/src/avatars/types/AvatarVisualDefinition.d.ts +6 -1
- package/esm/src/avatars/visuals/asciiOctopusAvatarVisual.d.ts +7 -0
- package/esm/src/avatars/visuals/octopus3AvatarVisual.d.ts +7 -0
- package/esm/src/avatars/visuals/octopusAvatarVisualShared.d.ts +125 -0
- package/esm/src/book-components/Chat/Chat/ChatMessageItem.d.ts +1 -1
- package/esm/src/book-components/Chat/Chat/ChatMessageList.d.ts +1 -1
- package/esm/src/book-components/Chat/Chat/ChatProps.d.ts +1 -1
- package/esm/src/book-components/Chat/Chat/ChatToolCallModalComponents.d.ts +8 -2
- package/esm/src/book-components/Chat/hooks/useChatCompleteNotification.d.ts +2 -0
- package/esm/src/book-components/Chat/types/ChatParticipant.d.ts +10 -0
- package/esm/src/cli/cli-commands/coder/ensureCoderGitignoreFile.d.ts +1 -1
- package/esm/src/config.d.ts +2 -2
- package/esm/src/llm-providers/agent/RemoteAgent.d.ts +3 -0
- package/esm/src/utils/agents/resolveAgentAvatarImageUrl.d.ts +49 -5
- package/esm/src/utils/agents/resolveAgentAvatarImageUrl.test.d.ts +1 -0
- package/esm/src/version.d.ts +1 -1
- package/package.json +4 -2
- package/umd/index.umd.js +688 -249
- package/umd/index.umd.js.map +1 -1
- package/umd/scripts/run-codex-prompts/common/waitForPause.d.ts +13 -1
- package/umd/scripts/run-codex-prompts/ui/buildCoderRunOctopusVisual.d.ts +13 -0
- package/umd/scripts/run-codex-prompts/ui/buildCoderRunUiFrame.d.ts +3 -1
- package/umd/scripts/run-codex-prompts/ui/coderRunUiRefresh.d.ts +8 -2
- package/umd/scripts/run-codex-prompts/ui/coderRunUiText.d.ts +36 -0
- package/umd/scripts/utils/emojiTags/scanEmojiTagUsage.d.ts +51 -0
- package/umd/src/avatars/AvatarOrImage.d.ts +45 -0
- package/umd/src/avatars/index.d.ts +1 -0
- package/umd/src/avatars/types/AvatarVisualDefinition.d.ts +6 -1
- package/umd/src/avatars/visuals/asciiOctopusAvatarVisual.d.ts +7 -0
- package/umd/src/avatars/visuals/octopus3AvatarVisual.d.ts +7 -0
- package/umd/src/avatars/visuals/octopusAvatarVisualShared.d.ts +125 -0
- package/umd/src/book-components/Chat/Chat/ChatMessageItem.d.ts +1 -1
- package/umd/src/book-components/Chat/Chat/ChatMessageList.d.ts +1 -1
- package/umd/src/book-components/Chat/Chat/ChatProps.d.ts +1 -1
- package/umd/src/book-components/Chat/Chat/ChatToolCallModalComponents.d.ts +8 -2
- package/umd/src/book-components/Chat/hooks/useChatCompleteNotification.d.ts +2 -0
- package/umd/src/book-components/Chat/types/ChatParticipant.d.ts +10 -0
- package/umd/src/cli/cli-commands/coder/ensureCoderGitignoreFile.d.ts +1 -1
- package/umd/src/config.d.ts +2 -2
- package/umd/src/llm-providers/agent/RemoteAgent.d.ts +3 -0
- package/umd/src/utils/agents/resolveAgentAvatarImageUrl.d.ts +49 -5
- package/umd/src/utils/agents/resolveAgentAvatarImageUrl.test.d.ts +1 -0
- package/umd/src/version.d.ts +1 -1
package/umd/index.umd.js
CHANGED
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
* @generated
|
|
61
61
|
* @see https://github.com/webgptorg/promptbook
|
|
62
62
|
*/
|
|
63
|
-
const PROMPTBOOK_ENGINE_VERSION = '0.112.0-
|
|
63
|
+
const PROMPTBOOK_ENGINE_VERSION = '0.112.0-47';
|
|
64
64
|
/**
|
|
65
65
|
* TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
|
|
66
66
|
* Note: [💞] Ignore a discrepancy between file name and entity name
|
|
@@ -1047,7 +1047,7 @@
|
|
|
1047
1047
|
*
|
|
1048
1048
|
* @public exported from `@promptbook/core`
|
|
1049
1049
|
*/
|
|
1050
|
-
const CLAIM = `
|
|
1050
|
+
const CLAIM = `Create persistent AI agents that turn your company's scattered knowledge into action`;
|
|
1051
1051
|
// <- TODO: [🐊] Pick the best claim
|
|
1052
1052
|
/**
|
|
1053
1053
|
* Color of the Promptbook
|
|
@@ -1100,7 +1100,7 @@
|
|
|
1100
1100
|
*
|
|
1101
1101
|
* @public exported from `@promptbook/core`
|
|
1102
1102
|
*/
|
|
1103
|
-
const DEFAULT_BOOK_TITLE =
|
|
1103
|
+
const DEFAULT_BOOK_TITLE = `🐙 Untitled agent`;
|
|
1104
1104
|
/**
|
|
1105
1105
|
* When the title of task is not provided, the default title is used
|
|
1106
1106
|
*
|
|
@@ -2490,37 +2490,68 @@
|
|
|
2490
2490
|
}
|
|
2491
2491
|
// Note: [🟡] Code for coder init environment bootstrapping [ensureCoderEnvFile](src/cli/cli-commands/coder/ensureCoderEnvFile.ts) should never be published outside of `@promptbook/cli`
|
|
2492
2492
|
|
|
2493
|
+
/**
|
|
2494
|
+
* @@@
|
|
2495
|
+
*
|
|
2496
|
+
* @public exported from `@promptbook/utils`
|
|
2497
|
+
*/
|
|
2498
|
+
function escapeRegExp$1(value) {
|
|
2499
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
2500
|
+
}
|
|
2501
|
+
|
|
2493
2502
|
/**
|
|
2494
2503
|
* Relative path to `.gitignore` in the initialized project.
|
|
2495
2504
|
*/
|
|
2496
2505
|
const GITIGNORE_FILE_PATH = '.gitignore';
|
|
2497
2506
|
/**
|
|
2498
|
-
*
|
|
2507
|
+
* Promptbook coder temp directory that should stay out of version control.
|
|
2508
|
+
*/
|
|
2509
|
+
const CODER_TEMP_GITIGNORE_RULE = '/.tmp';
|
|
2510
|
+
/**
|
|
2511
|
+
* Promptbook coder cache directory that should stay out of version control.
|
|
2499
2512
|
*/
|
|
2500
|
-
const
|
|
2501
|
-
# Promptbook Coder
|
|
2502
|
-
/.tmp
|
|
2503
|
-
`);
|
|
2513
|
+
const CODER_CACHE_GITIGNORE_RULE = '/.promptbook/ptbk-coder';
|
|
2504
2514
|
/**
|
|
2505
|
-
*
|
|
2515
|
+
* Standard header used when appending Promptbook coder rules into `.gitignore`.
|
|
2516
|
+
*/
|
|
2517
|
+
const CODER_GITIGNORE_HEADER = '# Promptbook Coder';
|
|
2518
|
+
/**
|
|
2519
|
+
* Ensures `.gitignore` contains the standalone Promptbook coder temp and cache entries.
|
|
2506
2520
|
*
|
|
2507
2521
|
* @private function of `initializeCoderProjectConfiguration`
|
|
2508
2522
|
*/
|
|
2509
2523
|
async function ensureCoderGitignoreFile(projectPath) {
|
|
2510
2524
|
const gitignorePath = path.join(projectPath, GITIGNORE_FILE_PATH);
|
|
2511
2525
|
const currentGitignoreContent = await readTextFileIfExists(gitignorePath);
|
|
2512
|
-
|
|
2526
|
+
const missingRules = getMissingCoderGitignoreRules(currentGitignoreContent || '');
|
|
2527
|
+
if (currentGitignoreContent !== undefined && missingRules.length === 0) {
|
|
2513
2528
|
return 'unchanged';
|
|
2514
2529
|
}
|
|
2515
|
-
const nextGitignoreContent = appendBlock(currentGitignoreContent || '',
|
|
2530
|
+
const nextGitignoreContent = appendBlock(currentGitignoreContent || '', buildCoderGitignoreBlock(missingRules));
|
|
2516
2531
|
await promises.writeFile(gitignorePath, nextGitignoreContent, 'utf-8');
|
|
2517
2532
|
return currentGitignoreContent === undefined ? 'created' : 'updated';
|
|
2518
2533
|
}
|
|
2519
2534
|
/**
|
|
2520
|
-
*
|
|
2535
|
+
* Returns the Promptbook coder gitignore rules that still need to be added.
|
|
2536
|
+
*/
|
|
2537
|
+
function getMissingCoderGitignoreRules(gitignoreContent) {
|
|
2538
|
+
const requiredRules = [CODER_TEMP_GITIGNORE_RULE, CODER_CACHE_GITIGNORE_RULE];
|
|
2539
|
+
return requiredRules.filter((rule) => !hasGitignoreRule(gitignoreContent, rule));
|
|
2540
|
+
}
|
|
2541
|
+
/**
|
|
2542
|
+
* Builds the Promptbook coder `.gitignore` block for the missing rules only.
|
|
2543
|
+
*/
|
|
2544
|
+
function buildCoderGitignoreBlock(missingRules) {
|
|
2545
|
+
return [CODER_GITIGNORE_HEADER, ...missingRules].join('\n');
|
|
2546
|
+
}
|
|
2547
|
+
/**
|
|
2548
|
+
* Detects whether `.gitignore` already covers one exact rule.
|
|
2521
2549
|
*/
|
|
2522
|
-
function
|
|
2523
|
-
|
|
2550
|
+
function hasGitignoreRule(gitignoreContent, rule) {
|
|
2551
|
+
const normalizedRulePattern = rule.startsWith('/')
|
|
2552
|
+
? `/?${escapeRegExp$1(rule.slice(1))}`
|
|
2553
|
+
: escapeRegExp$1(rule);
|
|
2554
|
+
return new RegExp(`(^|[\\r\\n])${normalizedRulePattern}(?:[\\r\\n]|$)`, 'u').test(gitignoreContent);
|
|
2524
2555
|
}
|
|
2525
2556
|
// Note: [🟡] Code for coder init gitignore bootstrapping [ensureCoderGitignoreFile](src/cli/cli-commands/coder/ensureCoderGitignoreFile.ts) should never be published outside of `@promptbook/cli`
|
|
2526
2557
|
|
|
@@ -2657,7 +2688,7 @@
|
|
|
2657
2688
|
${typescript.flattenDiagnosticMessageText(parsedFile.error.messageText, '\n')}
|
|
2658
2689
|
`));
|
|
2659
2690
|
}
|
|
2660
|
-
if (!isPlainObject(parsedFile.config)) {
|
|
2691
|
+
if (!isPlainObject$1(parsedFile.config)) {
|
|
2661
2692
|
throw new ParseError(_spaceTrim.spaceTrim(`
|
|
2662
2693
|
File \`${relativeFilePath}\` must contain one top-level JSON object.
|
|
2663
2694
|
`));
|
|
@@ -2671,7 +2702,7 @@
|
|
|
2671
2702
|
if (value === undefined) {
|
|
2672
2703
|
return {};
|
|
2673
2704
|
}
|
|
2674
|
-
if (!isPlainObject(value)) {
|
|
2705
|
+
if (!isPlainObject$1(value)) {
|
|
2675
2706
|
throw new ParseError(_spaceTrim.spaceTrim(`
|
|
2676
2707
|
File \`${relativeFilePath}\` contains invalid \`${fieldPath}\`.
|
|
2677
2708
|
|
|
@@ -2716,7 +2747,7 @@
|
|
|
2716
2747
|
/**
|
|
2717
2748
|
* Checks whether one parsed JSON value is a plain object.
|
|
2718
2749
|
*/
|
|
2719
|
-
function isPlainObject(value) {
|
|
2750
|
+
function isPlainObject$1(value) {
|
|
2720
2751
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
2721
2752
|
}
|
|
2722
2753
|
// Note: [🟡] Code for coder init JSON merging [mergeStringRecordJsonFile](src/cli/cli-commands/coder/mergeStringRecordJsonFile.ts) should never be published outside of `@promptbook/cli`
|
|
@@ -6196,15 +6227,6 @@
|
|
|
6196
6227
|
// TODO: [🧠][🈴] Where is the best location for this file
|
|
6197
6228
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
6198
6229
|
|
|
6199
|
-
/**
|
|
6200
|
-
* @@@
|
|
6201
|
-
*
|
|
6202
|
-
* @public exported from `@promptbook/utils`
|
|
6203
|
-
*/
|
|
6204
|
-
function escapeRegExp$1(value) {
|
|
6205
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
6206
|
-
}
|
|
6207
|
-
|
|
6208
6230
|
/**
|
|
6209
6231
|
* HTTP header used by Promptbook clients to advertise their release version.
|
|
6210
6232
|
*
|
|
@@ -43291,6 +43313,246 @@
|
|
|
43291
43313
|
// TODO: Mirror from Collboard or some common package
|
|
43292
43314
|
// Note: [💞] Ignore a discrepancy between file name and entity name
|
|
43293
43315
|
|
|
43316
|
+
/**
|
|
43317
|
+
* Default file globs scanned for emoji tags.
|
|
43318
|
+
*/
|
|
43319
|
+
const DEFAULT_INCLUDE_GLOBS = ['**/*.{ts,tsx,js,jsx,json,md,txt}'];
|
|
43320
|
+
/**
|
|
43321
|
+
* Default ignored paths while scanning the repository.
|
|
43322
|
+
*/
|
|
43323
|
+
const DEFAULT_IGNORE_GLOBS = ['**/node_modules/**', '**/.git/**', '**/.promptbook/ptbk-coder/**'];
|
|
43324
|
+
/**
|
|
43325
|
+
* Directory used for Promptbook coder runtime caches.
|
|
43326
|
+
*/
|
|
43327
|
+
const PTBK_CODER_CACHE_DIRECTORY_PATH = '.promptbook/ptbk-coder';
|
|
43328
|
+
/**
|
|
43329
|
+
* Relative cache file path storing per-file emoji-tag scan results.
|
|
43330
|
+
*/
|
|
43331
|
+
const EMOJI_TAG_SCAN_CACHE_FILE_PATH = `${PTBK_CODER_CACHE_DIRECTORY_PATH}/emoji-tag-scan-cache.json`;
|
|
43332
|
+
/**
|
|
43333
|
+
* Current schema version of the persisted emoji-tag scan cache.
|
|
43334
|
+
*/
|
|
43335
|
+
const EMOJI_TAG_SCAN_CACHE_VERSION = 1;
|
|
43336
|
+
/**
|
|
43337
|
+
* Scans repository files for bracketed emoji tags while reusing per-file cache entries for unchanged files.
|
|
43338
|
+
*/
|
|
43339
|
+
async function scanEmojiTagUsage(options) {
|
|
43340
|
+
var _a, _b, _c, _d;
|
|
43341
|
+
const rootDir = (_a = options.rootDir) !== null && _a !== void 0 ? _a : process.cwd();
|
|
43342
|
+
const includeGlobs = (_b = options.includeGlobs) !== null && _b !== void 0 ? _b : DEFAULT_INCLUDE_GLOBS;
|
|
43343
|
+
const ignoreGlobs = (_c = options.ignoreGlobs) !== null && _c !== void 0 ? _c : DEFAULT_IGNORE_GLOBS;
|
|
43344
|
+
const tagPrefix = (_d = options.tagPrefix) !== null && _d !== void 0 ? _d : '';
|
|
43345
|
+
const filesToScan = await findFilesToScan(rootDir, includeGlobs, ignoreGlobs);
|
|
43346
|
+
const matcher = buildEmojiTagMatcher(options.candidateEmojis, tagPrefix);
|
|
43347
|
+
const usedEmojis = new Set();
|
|
43348
|
+
const existingCache = await readEmojiTagScanCache(rootDir);
|
|
43349
|
+
const nextCacheFiles = { ...existingCache.files };
|
|
43350
|
+
let isCacheDirty = false;
|
|
43351
|
+
let scannedFileCount = 0;
|
|
43352
|
+
let reusedFileCount = 0;
|
|
43353
|
+
for (const filePath of filesToScan) {
|
|
43354
|
+
try {
|
|
43355
|
+
const fileStats = fs.statSync(filePath);
|
|
43356
|
+
const cacheKey = toCacheKey(rootDir, filePath);
|
|
43357
|
+
const cachedFile = nextCacheFiles[cacheKey];
|
|
43358
|
+
const cachedEmojis = getCachedFileEmojis(cachedFile, fileStats.mtimeMs, fileStats.size, tagPrefix);
|
|
43359
|
+
if (cachedEmojis) {
|
|
43360
|
+
reusedFileCount += 1;
|
|
43361
|
+
addEmojis(usedEmojis, cachedEmojis);
|
|
43362
|
+
continue;
|
|
43363
|
+
}
|
|
43364
|
+
const fileContent = fs.readFileSync(filePath, 'utf-8'); /* Note: sync file reads are fine for local tooling. */
|
|
43365
|
+
const scannedEmojis = scanFileForEmojiTags(fileContent, matcher);
|
|
43366
|
+
addEmojis(usedEmojis, scannedEmojis);
|
|
43367
|
+
nextCacheFiles[cacheKey] = updateCachedFile(cachedFile, fileStats.mtimeMs, fileStats.size, tagPrefix, scannedEmojis);
|
|
43368
|
+
scannedFileCount += 1;
|
|
43369
|
+
isCacheDirty = true;
|
|
43370
|
+
}
|
|
43371
|
+
catch (error) {
|
|
43372
|
+
if (options.onFileError) {
|
|
43373
|
+
options.onFileError(normalizeError(error), filePath);
|
|
43374
|
+
continue;
|
|
43375
|
+
}
|
|
43376
|
+
throw error;
|
|
43377
|
+
}
|
|
43378
|
+
}
|
|
43379
|
+
if (isCacheDirty) {
|
|
43380
|
+
await writeEmojiTagScanCache(rootDir, {
|
|
43381
|
+
version: EMOJI_TAG_SCAN_CACHE_VERSION,
|
|
43382
|
+
files: nextCacheFiles,
|
|
43383
|
+
});
|
|
43384
|
+
}
|
|
43385
|
+
return {
|
|
43386
|
+
usedEmojis,
|
|
43387
|
+
scannedFileCount,
|
|
43388
|
+
reusedFileCount,
|
|
43389
|
+
};
|
|
43390
|
+
}
|
|
43391
|
+
/**
|
|
43392
|
+
* Resolves files to scan for matching emoji tags.
|
|
43393
|
+
*/
|
|
43394
|
+
async function findFilesToScan(rootDir, includeGlobs, ignoreGlobs) {
|
|
43395
|
+
const files = new Set();
|
|
43396
|
+
for (const pattern of includeGlobs) {
|
|
43397
|
+
const matches = await glob__default["default"](pattern, {
|
|
43398
|
+
cwd: rootDir,
|
|
43399
|
+
ignore: Array.from(new Set([...ignoreGlobs, ...DEFAULT_IGNORE_GLOBS])),
|
|
43400
|
+
nodir: true,
|
|
43401
|
+
absolute: true,
|
|
43402
|
+
});
|
|
43403
|
+
for (const match of matches) {
|
|
43404
|
+
files.add(match);
|
|
43405
|
+
}
|
|
43406
|
+
}
|
|
43407
|
+
return Array.from(files);
|
|
43408
|
+
}
|
|
43409
|
+
/**
|
|
43410
|
+
* Builds a regex that matches one exact bracketed emoji-tag form.
|
|
43411
|
+
*/
|
|
43412
|
+
function buildEmojiTagMatcher(candidateEmojis, tagPrefix) {
|
|
43413
|
+
const escapedEmojiAlternatives = Array.from(candidateEmojis)
|
|
43414
|
+
.sort((leftEmoji, rightEmoji) => rightEmoji.length - leftEmoji.length)
|
|
43415
|
+
.map((emoji) => escapeRegExp$1(emoji))
|
|
43416
|
+
.join('|');
|
|
43417
|
+
if (escapedEmojiAlternatives === '') {
|
|
43418
|
+
return /$^/u;
|
|
43419
|
+
}
|
|
43420
|
+
return new RegExp(`\\[${escapeRegExp$1(tagPrefix)}(?<emoji>${escapedEmojiAlternatives})\\]`, 'gu');
|
|
43421
|
+
}
|
|
43422
|
+
/**
|
|
43423
|
+
* Extracts matching emojis from one file content.
|
|
43424
|
+
*/
|
|
43425
|
+
function scanFileForEmojiTags(fileContent, matcher) {
|
|
43426
|
+
var _a;
|
|
43427
|
+
matcher.lastIndex = 0;
|
|
43428
|
+
const matchedEmojis = new Set();
|
|
43429
|
+
for (const match of fileContent.matchAll(matcher)) {
|
|
43430
|
+
const emoji = (_a = match.groups) === null || _a === void 0 ? void 0 : _a.emoji;
|
|
43431
|
+
if (emoji) {
|
|
43432
|
+
matchedEmojis.add(emoji);
|
|
43433
|
+
}
|
|
43434
|
+
}
|
|
43435
|
+
return Array.from(matchedEmojis);
|
|
43436
|
+
}
|
|
43437
|
+
/**
|
|
43438
|
+
* Returns cached emojis when the file metadata still matches the stored cache entry.
|
|
43439
|
+
*/
|
|
43440
|
+
function getCachedFileEmojis(cachedFile, mtimeMs, size, tagPrefix) {
|
|
43441
|
+
if (!cachedFile || cachedFile.mtimeMs !== mtimeMs || cachedFile.size !== size) {
|
|
43442
|
+
return undefined;
|
|
43443
|
+
}
|
|
43444
|
+
const cachedEmojis = cachedFile.tagsByPrefix[tagPrefix];
|
|
43445
|
+
return Array.isArray(cachedEmojis) ? cachedEmojis : undefined;
|
|
43446
|
+
}
|
|
43447
|
+
/**
|
|
43448
|
+
* Stores the latest scan result for one file while preserving other prefix caches when the file revision is unchanged.
|
|
43449
|
+
*/
|
|
43450
|
+
function updateCachedFile(cachedFile, mtimeMs, size, tagPrefix, emojis) {
|
|
43451
|
+
const isSameFileRevision = (cachedFile === null || cachedFile === void 0 ? void 0 : cachedFile.mtimeMs) === mtimeMs && cachedFile.size === size;
|
|
43452
|
+
return {
|
|
43453
|
+
mtimeMs,
|
|
43454
|
+
size,
|
|
43455
|
+
tagsByPrefix: {
|
|
43456
|
+
...(isSameFileRevision ? cachedFile.tagsByPrefix : {}),
|
|
43457
|
+
[tagPrefix]: [...emojis],
|
|
43458
|
+
},
|
|
43459
|
+
};
|
|
43460
|
+
}
|
|
43461
|
+
/**
|
|
43462
|
+
* Loads the persisted emoji-tag scan cache and falls back to an empty cache when it is missing or invalid.
|
|
43463
|
+
*/
|
|
43464
|
+
async function readEmojiTagScanCache(rootDir) {
|
|
43465
|
+
try {
|
|
43466
|
+
const cacheContent = await promises.readFile(path.join(rootDir, EMOJI_TAG_SCAN_CACHE_FILE_PATH), 'utf-8');
|
|
43467
|
+
return normalizeEmojiTagScanCache(JSON.parse(cacheContent));
|
|
43468
|
+
}
|
|
43469
|
+
catch (_a) {
|
|
43470
|
+
return createEmptyEmojiTagScanCache();
|
|
43471
|
+
}
|
|
43472
|
+
}
|
|
43473
|
+
/**
|
|
43474
|
+
* Persists the updated emoji-tag scan cache as a best-effort optimization.
|
|
43475
|
+
*/
|
|
43476
|
+
async function writeEmojiTagScanCache(rootDir, cache) {
|
|
43477
|
+
try {
|
|
43478
|
+
await promises.mkdir(path.join(rootDir, PTBK_CODER_CACHE_DIRECTORY_PATH), { recursive: true });
|
|
43479
|
+
await promises.writeFile(path.join(rootDir, EMOJI_TAG_SCAN_CACHE_FILE_PATH), `${JSON.stringify(cache, null, 2)}\n`, 'utf-8');
|
|
43480
|
+
}
|
|
43481
|
+
catch (_a) {
|
|
43482
|
+
// Note: Cache writes are only an optimization; scanning still succeeds when the cache cannot be written.
|
|
43483
|
+
}
|
|
43484
|
+
}
|
|
43485
|
+
/**
|
|
43486
|
+
* Normalizes one parsed cache payload into the current typed cache shape.
|
|
43487
|
+
*/
|
|
43488
|
+
function normalizeEmojiTagScanCache(value) {
|
|
43489
|
+
if (!isPlainObject(value) || value.version !== EMOJI_TAG_SCAN_CACHE_VERSION || !isPlainObject(value.files)) {
|
|
43490
|
+
return createEmptyEmojiTagScanCache();
|
|
43491
|
+
}
|
|
43492
|
+
const files = {};
|
|
43493
|
+
for (const [filePath, cachedValue] of Object.entries(value.files)) {
|
|
43494
|
+
if (!isPlainObject(cachedValue)) {
|
|
43495
|
+
continue;
|
|
43496
|
+
}
|
|
43497
|
+
const { mtimeMs, size, tagsByPrefix } = cachedValue;
|
|
43498
|
+
if (typeof mtimeMs !== 'number' || typeof size !== 'number' || !isPlainObject(tagsByPrefix)) {
|
|
43499
|
+
continue;
|
|
43500
|
+
}
|
|
43501
|
+
const normalizedTagsByPrefix = {};
|
|
43502
|
+
for (const [tagPrefix, cachedEmojis] of Object.entries(tagsByPrefix)) {
|
|
43503
|
+
if (!Array.isArray(cachedEmojis)) {
|
|
43504
|
+
continue;
|
|
43505
|
+
}
|
|
43506
|
+
normalizedTagsByPrefix[tagPrefix] = cachedEmojis.filter((emoji) => typeof emoji === 'string');
|
|
43507
|
+
}
|
|
43508
|
+
files[filePath] = {
|
|
43509
|
+
mtimeMs,
|
|
43510
|
+
size,
|
|
43511
|
+
tagsByPrefix: normalizedTagsByPrefix,
|
|
43512
|
+
};
|
|
43513
|
+
}
|
|
43514
|
+
return {
|
|
43515
|
+
version: EMOJI_TAG_SCAN_CACHE_VERSION,
|
|
43516
|
+
files,
|
|
43517
|
+
};
|
|
43518
|
+
}
|
|
43519
|
+
/**
|
|
43520
|
+
* Creates an empty cache payload for emoji-tag scans.
|
|
43521
|
+
*/
|
|
43522
|
+
function createEmptyEmojiTagScanCache() {
|
|
43523
|
+
return {
|
|
43524
|
+
version: EMOJI_TAG_SCAN_CACHE_VERSION,
|
|
43525
|
+
files: {},
|
|
43526
|
+
};
|
|
43527
|
+
}
|
|
43528
|
+
/**
|
|
43529
|
+
* Converts an absolute file path into a stable cache key relative to the scanned root.
|
|
43530
|
+
*/
|
|
43531
|
+
function toCacheKey(rootDir, filePath) {
|
|
43532
|
+
return path.relative(rootDir, filePath).replace(/\\/gu, '/');
|
|
43533
|
+
}
|
|
43534
|
+
/**
|
|
43535
|
+
* Adds multiple emojis into one target set.
|
|
43536
|
+
*/
|
|
43537
|
+
function addEmojis(target, emojis) {
|
|
43538
|
+
for (const emoji of emojis) {
|
|
43539
|
+
target.add(emoji);
|
|
43540
|
+
}
|
|
43541
|
+
}
|
|
43542
|
+
/**
|
|
43543
|
+
* Checks whether one unknown JSON value is a plain object.
|
|
43544
|
+
*/
|
|
43545
|
+
function isPlainObject(value) {
|
|
43546
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
43547
|
+
}
|
|
43548
|
+
/**
|
|
43549
|
+
* Normalizes thrown values into proper `Error` objects for optional callbacks.
|
|
43550
|
+
*/
|
|
43551
|
+
function normalizeError(error) {
|
|
43552
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
43553
|
+
}
|
|
43554
|
+
// Note: [?] Code in this file should never be published in any package
|
|
43555
|
+
|
|
43294
43556
|
// find-fresh-emoji-tags.ts
|
|
43295
43557
|
// Note: When run as a standalone script, call the exported function
|
|
43296
43558
|
if (require.main === module) {
|
|
@@ -43320,29 +43582,16 @@
|
|
|
43320
43582
|
async function findFreshEmojiTag() {
|
|
43321
43583
|
initializeFindFreshEmojiTagRun();
|
|
43322
43584
|
console.info(`🤪 Find fresh emoji tag`);
|
|
43323
|
-
const allFiles = await glob__default["default"]('**/*.{ts,tsx,js,jsx,json,md,txt}', {
|
|
43324
|
-
ignore: '**/node_modules/**', // <- TODO: [🚰] Ignore also hidden folders like *(`.promptbook`, `.next`, `.git`,...)*
|
|
43325
|
-
});
|
|
43326
43585
|
const allEmojis = EMOJIS_OF_SINGLE_PICTOGRAM;
|
|
43327
|
-
|
|
43328
|
-
|
|
43329
|
-
|
|
43330
|
-
|
|
43331
|
-
|
|
43332
|
-
for (const emoji of allEmojis) {
|
|
43333
|
-
const tag = `[${emoji}]`;
|
|
43334
|
-
if (content.includes(tag)) {
|
|
43335
|
-
usedEmojis.add(emoji);
|
|
43336
|
-
}
|
|
43337
|
-
}
|
|
43338
|
-
}
|
|
43339
|
-
catch (error) {
|
|
43340
|
-
console.error(colors__default["default"].red('Error in checking file file /' + file));
|
|
43586
|
+
const { usedEmojis } = await scanEmojiTagUsage({
|
|
43587
|
+
candidateEmojis: allEmojis,
|
|
43588
|
+
tagPrefix: '',
|
|
43589
|
+
onFileError: (error, filePath) => {
|
|
43590
|
+
console.error(colors__default["default"].red('Error in checking file /' + filePath));
|
|
43341
43591
|
console.error(error);
|
|
43342
|
-
}
|
|
43343
|
-
}
|
|
43344
|
-
|
|
43345
|
-
const freshEmojis = difference(allEmojis, usedEmojis);
|
|
43592
|
+
},
|
|
43593
|
+
});
|
|
43594
|
+
const freshEmojis = new Set(Array.from(allEmojis).filter((emoji) => !usedEmojis.has(emoji)));
|
|
43346
43595
|
console.info(colors__default["default"].green(`Avialable fresh tags:`));
|
|
43347
43596
|
const randomEmojis = [...$shuffleItems(...Array.from(freshEmojis))].splice(0, 10);
|
|
43348
43597
|
// const randomEmojis = freshEmojis;
|
|
@@ -43950,9 +44199,14 @@
|
|
|
43950
44199
|
const includeGlobs = (_b = options.includeGlobs) !== null && _b !== void 0 ? _b : ['**/*.{ts,tsx,js,jsx,json,md,txt}'];
|
|
43951
44200
|
const ignoreGlobs = (_c = options.ignoreGlobs) !== null && _c !== void 0 ? _c : ['**/node_modules/**'];
|
|
43952
44201
|
const tagPrefix = (_d = options.tagPrefix) !== null && _d !== void 0 ? _d : PROMPT_EMOJI_TAG_PREFIX;
|
|
43953
|
-
const
|
|
43954
|
-
|
|
43955
|
-
|
|
44202
|
+
const { usedEmojis } = await scanEmojiTagUsage({
|
|
44203
|
+
rootDir,
|
|
44204
|
+
includeGlobs,
|
|
44205
|
+
ignoreGlobs,
|
|
44206
|
+
tagPrefix,
|
|
44207
|
+
candidateEmojis: EMOJIS_OF_SINGLE_PICTOGRAM,
|
|
44208
|
+
});
|
|
44209
|
+
const freshEmojis = new Set(Array.from(EMOJIS_OF_SINGLE_PICTOGRAM).filter((emoji) => !usedEmojis.has(emoji)));
|
|
43956
44210
|
const shuffledEmojis = $shuffleItems(...Array.from(freshEmojis));
|
|
43957
44211
|
const selectedEmojis = shuffledEmojis.slice(0, count);
|
|
43958
44212
|
if (selectedEmojis.length < count) {
|
|
@@ -43964,40 +44218,6 @@
|
|
|
43964
44218
|
tagPrefix,
|
|
43965
44219
|
};
|
|
43966
44220
|
}
|
|
43967
|
-
/**
|
|
43968
|
-
* Resolves files to scan for existing prompt emoji tags.
|
|
43969
|
-
*/
|
|
43970
|
-
async function findFilesToScan(rootDir, includeGlobs, ignoreGlobs) {
|
|
43971
|
-
const files = new Set();
|
|
43972
|
-
for (const pattern of includeGlobs) {
|
|
43973
|
-
const matches = await glob__default["default"](pattern, {
|
|
43974
|
-
cwd: rootDir,
|
|
43975
|
-
ignore: ignoreGlobs,
|
|
43976
|
-
nodir: true,
|
|
43977
|
-
absolute: true,
|
|
43978
|
-
});
|
|
43979
|
-
for (const match of matches) {
|
|
43980
|
-
files.add(match);
|
|
43981
|
-
}
|
|
43982
|
-
}
|
|
43983
|
-
return Array.from(files);
|
|
43984
|
-
}
|
|
43985
|
-
/**
|
|
43986
|
-
* Collects emojis already used with the configured prompt prefix.
|
|
43987
|
-
*/
|
|
43988
|
-
function collectUsedPromptEmojis(filePaths, tagPrefix) {
|
|
43989
|
-
const usedEmojis = new Set();
|
|
43990
|
-
for (const file of filePaths) {
|
|
43991
|
-
const content = fs.readFileSync(file, 'utf-8'); /* Note: sync read is fine for script tooling. */
|
|
43992
|
-
for (const emoji of EMOJIS_OF_SINGLE_PICTOGRAM) {
|
|
43993
|
-
const tag = formatPromptEmojiTag(emoji, tagPrefix);
|
|
43994
|
-
if (content.includes(tag)) {
|
|
43995
|
-
usedEmojis.add(emoji);
|
|
43996
|
-
}
|
|
43997
|
-
}
|
|
43998
|
-
}
|
|
43999
|
-
return usedEmojis;
|
|
44000
|
-
}
|
|
44001
44221
|
// Note: [?] Code in this file should never be published in any package
|
|
44002
44222
|
|
|
44003
44223
|
var promptEmojiTags = /*#__PURE__*/Object.freeze({
|
|
@@ -45026,6 +45246,27 @@
|
|
|
45026
45246
|
* Current pause state.
|
|
45027
45247
|
*/
|
|
45028
45248
|
let pauseState = 'RUNNING';
|
|
45249
|
+
/**
|
|
45250
|
+
* Stores one new pause state in the shared runner controller.
|
|
45251
|
+
*/
|
|
45252
|
+
function setPauseState(nextPauseState) {
|
|
45253
|
+
pauseState = nextPauseState;
|
|
45254
|
+
}
|
|
45255
|
+
/**
|
|
45256
|
+
* Applies the same three-state toggle used by the `P` hotkey.
|
|
45257
|
+
*/
|
|
45258
|
+
function togglePauseState() {
|
|
45259
|
+
if (pauseState === 'RUNNING') {
|
|
45260
|
+
setPauseState('PAUSING');
|
|
45261
|
+
return 'REQUESTED_PAUSE';
|
|
45262
|
+
}
|
|
45263
|
+
if (pauseState === 'PAUSING') {
|
|
45264
|
+
setPauseState('RUNNING');
|
|
45265
|
+
return 'CANCELLED_PAUSE';
|
|
45266
|
+
}
|
|
45267
|
+
setPauseState('RUNNING');
|
|
45268
|
+
return 'RESUMED';
|
|
45269
|
+
}
|
|
45029
45270
|
/**
|
|
45030
45271
|
* Listens for the "p" key to pause and resume.
|
|
45031
45272
|
*/
|
|
@@ -45040,19 +45281,13 @@
|
|
|
45040
45281
|
process.exit();
|
|
45041
45282
|
}
|
|
45042
45283
|
if (key.name === 'p') {
|
|
45043
|
-
|
|
45044
|
-
|
|
45284
|
+
const toggleResult = togglePauseState();
|
|
45285
|
+
if (toggleResult === 'REQUESTED_PAUSE') {
|
|
45045
45286
|
// Note: Using console.log here which adds a new line.
|
|
45046
45287
|
// This is intentional to prevent the message from being overwritten.
|
|
45047
45288
|
console.log(colors__default["default"].bgWhite('Pausing...'));
|
|
45048
45289
|
}
|
|
45049
|
-
else if (
|
|
45050
|
-
pauseState = 'RUNNING';
|
|
45051
|
-
// The checkPause loop will terminate.
|
|
45052
|
-
}
|
|
45053
|
-
else if (pauseState === 'PAUSING') {
|
|
45054
|
-
// If user presses 'p' again while pausing, cancel the pause.
|
|
45055
|
-
pauseState = 'RUNNING';
|
|
45290
|
+
else if (toggleResult === 'CANCELLED_PAUSE') {
|
|
45056
45291
|
console.log(colors__default["default"].green('Pause cancelled. Resuming...'));
|
|
45057
45292
|
}
|
|
45058
45293
|
}
|
|
@@ -45068,12 +45303,12 @@
|
|
|
45068
45303
|
async function checkPause(options) {
|
|
45069
45304
|
var _a, _b;
|
|
45070
45305
|
if (pauseState === 'PAUSING') {
|
|
45071
|
-
|
|
45306
|
+
setPauseState('PAUSED');
|
|
45072
45307
|
if (!(options === null || options === void 0 ? void 0 : options.silent)) {
|
|
45073
45308
|
console.log(colors__default["default"].bgWhite.black('Paused') + colors__default["default"].gray(' (Press "p" to resume)'));
|
|
45074
45309
|
}
|
|
45075
45310
|
(_a = options === null || options === void 0 ? void 0 : options.onPaused) === null || _a === void 0 ? void 0 : _a.call(options);
|
|
45076
|
-
while (
|
|
45311
|
+
while (getPauseState() === 'PAUSED') {
|
|
45077
45312
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
45078
45313
|
}
|
|
45079
45314
|
(_b = options === null || options === void 0 ? void 0 : options.onResumed) === null || _b === void 0 ? void 0 : _b.call(options);
|
|
@@ -45088,20 +45323,6 @@
|
|
|
45088
45323
|
function getPauseState() {
|
|
45089
45324
|
return pauseState;
|
|
45090
45325
|
}
|
|
45091
|
-
/**
|
|
45092
|
-
* Requests a pause from an external controller (e.g. the Ink UI).
|
|
45093
|
-
*/
|
|
45094
|
-
function requestPause() {
|
|
45095
|
-
if (pauseState === 'RUNNING') {
|
|
45096
|
-
pauseState = 'PAUSING';
|
|
45097
|
-
}
|
|
45098
|
-
}
|
|
45099
|
-
/**
|
|
45100
|
-
* Resumes execution from an external controller after a pause.
|
|
45101
|
-
*/
|
|
45102
|
-
function requestResume() {
|
|
45103
|
-
pauseState = 'RUNNING';
|
|
45104
|
-
}
|
|
45105
45326
|
|
|
45106
45327
|
/**
|
|
45107
45328
|
* Environment variable that configures the name used for agent commits.
|
|
@@ -48741,49 +48962,215 @@ bash "$1"
|
|
|
48741
48962
|
return `${scriptPath}.test.sh`;
|
|
48742
48963
|
}
|
|
48743
48964
|
|
|
48965
|
+
/**
|
|
48966
|
+
* Centers an ANSI-colored line within the available frame width.
|
|
48967
|
+
*
|
|
48968
|
+
* @private internal utility of coder run UI
|
|
48969
|
+
*/
|
|
48970
|
+
function centerAnsiText(text, width) {
|
|
48971
|
+
const paddingWidth = Math.max(0, Math.floor((width - visibleLength(text)) / 2));
|
|
48972
|
+
return `${' '.repeat(paddingWidth)}${text}`;
|
|
48973
|
+
}
|
|
48974
|
+
/**
|
|
48975
|
+
* Pads or truncates a possibly ANSI-colored line to the target visible width.
|
|
48976
|
+
*
|
|
48977
|
+
* @private internal utility of coder run UI
|
|
48978
|
+
*/
|
|
48979
|
+
function padAnsiText(text, width) {
|
|
48980
|
+
const fittedText = fitAnsiText(text, width);
|
|
48981
|
+
return fittedText + ' '.repeat(Math.max(0, width - visibleLength(fittedText)));
|
|
48982
|
+
}
|
|
48983
|
+
/**
|
|
48984
|
+
* Truncates a possibly ANSI-colored line to the target visible width.
|
|
48985
|
+
*
|
|
48986
|
+
* @private internal utility of coder run UI
|
|
48987
|
+
*/
|
|
48988
|
+
function fitAnsiText(text, width) {
|
|
48989
|
+
if (visibleLength(text) <= width) {
|
|
48990
|
+
return text;
|
|
48991
|
+
}
|
|
48992
|
+
return fitPlainText(stripAnsi(text), width);
|
|
48993
|
+
}
|
|
48994
|
+
/**
|
|
48995
|
+
* Truncates a plain-text line to the target width with an ellipsis.
|
|
48996
|
+
*
|
|
48997
|
+
* @private internal utility of coder run UI
|
|
48998
|
+
*/
|
|
48999
|
+
function fitPlainText(text, width) {
|
|
49000
|
+
if (text.length <= width) {
|
|
49001
|
+
return text;
|
|
49002
|
+
}
|
|
49003
|
+
if (width <= 3) {
|
|
49004
|
+
return '.'.repeat(width);
|
|
49005
|
+
}
|
|
49006
|
+
return `${text.slice(0, width - 3)}...`;
|
|
49007
|
+
}
|
|
49008
|
+
/**
|
|
49009
|
+
* Measures visible string width by stripping ANSI escape codes.
|
|
49010
|
+
*
|
|
49011
|
+
* @private internal utility of coder run UI
|
|
49012
|
+
*/
|
|
49013
|
+
function visibleLength(text) {
|
|
49014
|
+
return stripAnsi(text).length;
|
|
49015
|
+
}
|
|
49016
|
+
/**
|
|
49017
|
+
* Strips ANSI escape codes from a string.
|
|
49018
|
+
*
|
|
49019
|
+
* @private internal utility of coder run UI
|
|
49020
|
+
*/
|
|
49021
|
+
function stripAnsi(text) {
|
|
49022
|
+
// eslint-disable-next-line no-control-regex
|
|
49023
|
+
return text.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, '');
|
|
49024
|
+
}
|
|
49025
|
+
|
|
49026
|
+
/**
|
|
49027
|
+
* Fixed left-side head used across all octopus frames.
|
|
49028
|
+
*/
|
|
49029
|
+
const OCTOPUS_HEAD_LINES = [
|
|
49030
|
+
colors__default["default"].magenta.bold(' .-""""-.'),
|
|
49031
|
+
colors__default["default"].magenta.bold(" .' .-. '."),
|
|
49032
|
+
`${colors__default["default"].magenta.bold(' / (')}${colors__default["default"].yellow.bold('o o')}${colors__default["default"].magenta.bold(') \\')}`,
|
|
49033
|
+
colors__default["default"].magenta.bold(' | ^ |'),
|
|
49034
|
+
colors__default["default"].magenta.bold(' | \\___/ |'),
|
|
49035
|
+
colors__default["default"].magenta.bold(' \\___________/'),
|
|
49036
|
+
];
|
|
49037
|
+
/**
|
|
49038
|
+
* Gap between the head silhouette and the animated tentacles.
|
|
49039
|
+
*/
|
|
49040
|
+
const OCTOPUS_HEAD_TO_TENTACLE_GAP = ' ';
|
|
49041
|
+
/**
|
|
49042
|
+
* Animated right-side tentacle poses.
|
|
49043
|
+
*/
|
|
49044
|
+
const OCTOPUS_TENTACLE_FRAMES = [
|
|
49045
|
+
[
|
|
49046
|
+
` ${colors__default["default"].green.bold('ptbk.io')}`,
|
|
49047
|
+
colors__default["default"].cyan(' __ __'),
|
|
49048
|
+
colors__default["default"].cyan(' _/\\/\\/ \\_/\\/ \\_/\\_'),
|
|
49049
|
+
colors__default["default"].cyan('_/\\/ _ /\\/ _ /\\/\\__'),
|
|
49050
|
+
colors__default["default"].cyan('\\__/ /_/\\/ \\_/ \\_/ \\_/'),
|
|
49051
|
+
colors__default["default"].cyan(' /_/ \\__/ \\__/ /'),
|
|
49052
|
+
],
|
|
49053
|
+
[
|
|
49054
|
+
` ${colors__default["default"].green.bold('ptbk.io')}`,
|
|
49055
|
+
colors__default["default"].cyan(' __ __'),
|
|
49056
|
+
colors__default["default"].cyan(' _/\\/ \\_/\\/\\_ \\_/\\_'),
|
|
49057
|
+
colors__default["default"].cyan('_/\\/ _ /\\/ _ \\/\\/ \\__'),
|
|
49058
|
+
colors__default["default"].cyan('\\__/ /_/\\/ \\_/ \\_ /\\_/'),
|
|
49059
|
+
colors__default["default"].cyan(' /_/ \\__/ \\__/ /'),
|
|
49060
|
+
],
|
|
49061
|
+
[
|
|
49062
|
+
` ${colors__default["default"].green.bold('ptbk.io')}`,
|
|
49063
|
+
colors__default["default"].cyan(' __ __'),
|
|
49064
|
+
colors__default["default"].cyan(' _/\\/\\/ \\_/\\_ \\_/\\_'),
|
|
49065
|
+
colors__default["default"].cyan('_/\\/ _ /\\/ _ /\\/ \\_'),
|
|
49066
|
+
colors__default["default"].cyan('\\__/ /_/\\/ \\_/ \\_/ _/'),
|
|
49067
|
+
colors__default["default"].cyan(' /_/ \\__/ \\__/'),
|
|
49068
|
+
],
|
|
49069
|
+
[
|
|
49070
|
+
` ${colors__default["default"].green.bold('ptbk.io')}`,
|
|
49071
|
+
colors__default["default"].cyan(' __ __'),
|
|
49072
|
+
colors__default["default"].cyan(' _/\\/ \\_/\\/ \\_/\\/\\_'),
|
|
49073
|
+
colors__default["default"].cyan('_/\\/ _ /\\/ _ /\\/\\ \\'),
|
|
49074
|
+
colors__default["default"].cyan('\\__/ /_/\\/ \\_/ \\_ \\_/'),
|
|
49075
|
+
colors__default["default"].cyan(' /_/ \\__/ \\__\\_/'),
|
|
49076
|
+
],
|
|
49077
|
+
];
|
|
49078
|
+
/**
|
|
49079
|
+
* Builds the horizontal octopus illustration shown above the coder-run dashboard.
|
|
49080
|
+
*
|
|
49081
|
+
* @private internal utility of coder run UI
|
|
49082
|
+
*/
|
|
49083
|
+
function buildCoderRunOctopusVisual(options) {
|
|
49084
|
+
const tentacleFrame = OCTOPUS_TENTACLE_FRAMES[((options.animationFrame % OCTOPUS_TENTACLE_FRAMES.length) + OCTOPUS_TENTACLE_FRAMES.length) %
|
|
49085
|
+
OCTOPUS_TENTACLE_FRAMES.length];
|
|
49086
|
+
const visualLines = OCTOPUS_HEAD_LINES.map((headLine, lineIndex) => `${headLine}${OCTOPUS_HEAD_TO_TENTACLE_GAP}${tentacleFrame[lineIndex]}`);
|
|
49087
|
+
const visualWidth = visualLines.reduce((maxWidth, line) => Math.max(maxWidth, visibleLength(line)), 0);
|
|
49088
|
+
visualLines.map((line) => centerAnsiText(padAnsiText(line, visualWidth), options.totalWidth));
|
|
49089
|
+
/*
|
|
49090
|
+
Note: Octopus art should look better, now using just text
|
|
49091
|
+
https://www.google.com/search?sca_esv=52e84acb78c558cc&sxsrf=ANbL-n7DVKf71T1HSPRpM-2skfMss0jh7w:1776693767588&udm=2&fbs=ADc_l-ZseckkBJUFopaGDNYa-HGjo4_b6b_a7pIHTL5Y9QnExg6xJqXbG7aOLcH8CWqOtkzCrjxXWZVmrIhYPvZzFDVUIb7oTJfuJ6idsCc5GA1j5KGoi2q3sW0uDBWWfYgbuxGWTQPZMetvj33BdP833wZm47mxW-6rC3bTQWluwJdOsgloPieyQvTfF2uNgIZ_K0KZ-WzpL1An8GuRrKqHdvl8T306FA&q=octopus&sa=X&ved=2ahUKEwil7ZCHzPyTAxUghP0HHY-_Js8QtKgLegQIOhAB&biw=1745&bih=903&dpr=1.1#sv=CAMSVhoyKhBlLVl1bHNmeVhneml6a1dNMg5ZdWxzZnlYZ3ppemtXTToOTVJvdXhTdk5STkZ4d00gBCocCgZtb3NhaWMSEGUtWXVsc2Z5WGd6aXprV00YADABGAcg46LBxA9KCBABGAEgASgB
|
|
49092
|
+
https://www.mrgoodfish.com/wp-content/uploads/2022/09/Eledone_moschata__.png
|
|
49093
|
+
https://fish-commercial-names.ec.europa.eu/fish-names/jakarta.faces.resource/pictograms/octopus_vulgaris.jpg.xhtml?ln=images
|
|
49094
|
+
https://www.asciiart.eu/image-to-ascii
|
|
49095
|
+
*/
|
|
49096
|
+
// Note: Created by https://patorjk.com/software/taag/#p=display&f=ANSI+Compact&t=ptbk.io&x=none&v=4&h=4&w=80&we=false
|
|
49097
|
+
return _spaceTrim__default["default"](`
|
|
49098
|
+
|
|
49099
|
+
▄▄▄▄ ▄▄▄▄▄▄ ▄▄▄▄ ▄▄ ▄▄ ▄▄ ▄▄▄
|
|
49100
|
+
██▄█▀ ██ ██▄██ ██▄█▀ ██ ██▀██
|
|
49101
|
+
██ ██ ██▄█▀ ██ ██ ▄ ██ ▀███▀
|
|
49102
|
+
|
|
49103
|
+
`)
|
|
49104
|
+
.split('\n')
|
|
49105
|
+
.map((line) => centerAnsiText(line, options.totalWidth));
|
|
49106
|
+
}
|
|
49107
|
+
|
|
49108
|
+
/**
|
|
49109
|
+
* Refresh cadence used only while the rich coder UI needs animated updates.
|
|
49110
|
+
*
|
|
49111
|
+
* @private internal constant of coder run UI
|
|
49112
|
+
*/
|
|
49113
|
+
const ACTIVE_CODER_RUN_UI_REFRESH_INTERVAL_MS = 300;
|
|
49114
|
+
/**
|
|
49115
|
+
* Phases that still benefit from automatic refreshes because the frame can change
|
|
49116
|
+
* over time even without new runner output.
|
|
49117
|
+
*
|
|
49118
|
+
* @private internal constant of coder run UI
|
|
49119
|
+
*/
|
|
49120
|
+
const AUTO_REFRESH_PHASES = ['initializing', 'loading', 'running', 'verifying'];
|
|
49121
|
+
/**
|
|
49122
|
+
* Returns whether the rich coder UI should keep animating on its own.
|
|
49123
|
+
*
|
|
49124
|
+
* @private internal utility of coder run UI
|
|
49125
|
+
*/
|
|
49126
|
+
function isCoderRunUiAutoRefreshing(phase, pauseState) {
|
|
49127
|
+
// `PAUSING` still means the current task is winding down, so keep active
|
|
49128
|
+
// animations/timers running until the runner reaches the fully paused state.
|
|
49129
|
+
if (pauseState === 'PAUSED') {
|
|
49130
|
+
return false;
|
|
49131
|
+
}
|
|
49132
|
+
return AUTO_REFRESH_PHASES.includes(phase);
|
|
49133
|
+
}
|
|
49134
|
+
/**
|
|
49135
|
+
* Returns the automatic refresh interval for the current UI state.
|
|
49136
|
+
*
|
|
49137
|
+
* Waiting, paused, and completed states return `undefined` so the rich UI stays
|
|
49138
|
+
* perfectly still until actual state changes arrive.
|
|
49139
|
+
*
|
|
49140
|
+
* @private internal utility of coder run UI
|
|
49141
|
+
*/
|
|
49142
|
+
function getCoderRunUiAutoRefreshInterval(phase, pauseState) {
|
|
49143
|
+
return isCoderRunUiAutoRefreshing(phase, pauseState) ? ACTIVE_CODER_RUN_UI_REFRESH_INTERVAL_MS : undefined;
|
|
49144
|
+
}
|
|
49145
|
+
|
|
48744
49146
|
/**
|
|
48745
49147
|
* Maximum number of output lines reserved for agent output in the UI.
|
|
48746
49148
|
*/
|
|
48747
49149
|
const MAX_VISIBLE_OUTPUT_LINES = 8;
|
|
49150
|
+
/**
|
|
49151
|
+
* Minimum width used for the rich coder-run frame.
|
|
49152
|
+
*/
|
|
49153
|
+
const MIN_FRAME_WIDTH = 56;
|
|
49154
|
+
/**
|
|
49155
|
+
* Maximum width used for the rich coder-run frame.
|
|
49156
|
+
*/
|
|
49157
|
+
const MAX_FRAME_WIDTH = 96;
|
|
49158
|
+
/**
|
|
49159
|
+
* Visible width reserved for aligned labels in the session box.
|
|
49160
|
+
*/
|
|
49161
|
+
const SESSION_LABEL_WIDTH = 8;
|
|
48748
49162
|
/**
|
|
48749
49163
|
* Builds the complete boxed terminal frame for the rich `ptbk coder run` UI.
|
|
48750
49164
|
*/
|
|
48751
49165
|
function buildCoderRunUiFrame(options) {
|
|
48752
|
-
const totalWidth = Math.max(
|
|
49166
|
+
const totalWidth = Math.max(MIN_FRAME_WIDTH, Math.min(options.terminalWidth, MAX_FRAME_WIDTH));
|
|
48753
49167
|
const isPromptActive = options.phase === 'running' || options.phase === 'verifying' || options.phase === 'loading';
|
|
48754
49168
|
const promptStatusPrefix = isPromptActive ? `${colors__default["default"].yellow(`${options.spinner} `)}` : '';
|
|
48755
|
-
const
|
|
48756
|
-
?
|
|
48757
|
-
:
|
|
48758
|
-
const
|
|
48759
|
-
const
|
|
48760
|
-
if (options.progress.skippedPrompts > 0) {
|
|
48761
|
-
sessionQueueParts.push(`Skipping ${formatPromptCount(options.progress.skippedPrompts)} with Priority <${options.config.priority}`);
|
|
48762
|
-
}
|
|
48763
|
-
if (options.progress.toBeWrittenPrompts > 0) {
|
|
48764
|
-
sessionQueueParts.push(`Write first ${formatPromptCount(options.progress.toBeWrittenPrompts)}`);
|
|
48765
|
-
}
|
|
48766
|
-
const sessionLines = [
|
|
48767
|
-
`${buildPhaseBadge(options.phase, options.pauseState)} ${fitPlainText(options.statusMessage, totalWidth - 18)}`,
|
|
48768
|
-
sessionScopeLine,
|
|
48769
|
-
sessionCountLine,
|
|
48770
|
-
...(sessionQueueParts.length > 0 ? [sessionQueueParts.join(' · ')] : []),
|
|
48771
|
-
`Elapsed ${options.progress.elapsedText} · Est. total ${options.progress.estimatedTotalText} · Est. done ${options.progress.estimatedLabel}`,
|
|
48772
|
-
buildProgressBar(options.progress.percentage, totalWidth - 6, `${options.progress.percentage}% complete (${options.progress.sessionDone}/${options.progress.sessionTotal} done)`),
|
|
48773
|
-
];
|
|
48774
|
-
const metadataParts = [options.config.agentName || 'No agent selected'];
|
|
48775
|
-
if (options.config.modelName) {
|
|
48776
|
-
metadataParts.push(options.config.modelName);
|
|
48777
|
-
}
|
|
48778
|
-
if (options.config.thinkingLevel) {
|
|
48779
|
-
metadataParts.push(`thinking ${options.config.thinkingLevel}`);
|
|
48780
|
-
}
|
|
48781
|
-
const runnerDetails = [
|
|
48782
|
-
[`${colors__default["default"].bgCyan.black(' PTBK ')}`, colors__default["default"].bgBlue.white(' CODER '), colors__default["default"].bold.white(' Promptbook Coder')]
|
|
48783
|
-
.join(''),
|
|
48784
|
-
metadataParts.join(' · '),
|
|
48785
|
-
buildConfigSummaryLine(options.config),
|
|
48786
|
-
];
|
|
49169
|
+
const octopusAnimationFrame = isCoderRunUiAutoRefreshing(options.phase, options.pauseState)
|
|
49170
|
+
? options.animationFrame
|
|
49171
|
+
: 0;
|
|
49172
|
+
const pausePresentation = buildPausePresentation(options.phase, options.pauseState, options.statusMessage);
|
|
49173
|
+
const sessionLines = buildSessionLines(options, totalWidth, pausePresentation);
|
|
48787
49174
|
const currentTaskLines = options.currentPromptLabel
|
|
48788
49175
|
? [
|
|
48789
49176
|
`${promptStatusPrefix}${colors__default["default"].bold.white(fitPlainText(options.currentPromptLabel, totalWidth - 8))}`,
|
|
@@ -48792,9 +49179,10 @@ bash "$1"
|
|
|
48792
49179
|
]
|
|
48793
49180
|
: [options.statusMessage, ...options.detailLines.map((detailLine) => `• ${detailLine}`)];
|
|
48794
49181
|
const visibleOutputLines = buildVisibleOutputLines(options.agentOutputLines);
|
|
48795
|
-
const controls = buildControlPills(
|
|
49182
|
+
const controls = buildControlPills(pausePresentation.pauseControl, options.pendingEnterLabel).join(' ');
|
|
48796
49183
|
const frame = [
|
|
48797
|
-
...
|
|
49184
|
+
...buildCoderRunOctopusVisual({ totalWidth, animationFrame: octopusAnimationFrame }),
|
|
49185
|
+
'',
|
|
48798
49186
|
...renderBox('Session', sessionLines, totalWidth, colors__default["default"].yellow.bold),
|
|
48799
49187
|
...renderBox(options.currentPromptLabel ? 'Current task' : 'Queue', currentTaskLines, totalWidth, colors__default["default"].magenta.bold),
|
|
48800
49188
|
...renderBox('Live output', visibleOutputLines, totalWidth, colors__default["default"].green.bold),
|
|
@@ -48805,13 +49193,67 @@ bash "$1"
|
|
|
48805
49193
|
frame.push(...renderBox('Controls', [controls], totalWidth, colors__default["default"].white.bold));
|
|
48806
49194
|
return frame;
|
|
48807
49195
|
}
|
|
49196
|
+
/**
|
|
49197
|
+
* Builds the structured session lines that combine state, runner, queue, and timing metadata.
|
|
49198
|
+
*/
|
|
49199
|
+
function buildSessionLines(options, totalWidth, pausePresentation) {
|
|
49200
|
+
const bodyWidth = Math.max(10, totalWidth - 4);
|
|
49201
|
+
return buildSessionRows(options, bodyWidth, pausePresentation).map((sessionRow) => buildLabeledSessionLine(sessionRow.label, sessionRow.value, bodyWidth));
|
|
49202
|
+
}
|
|
49203
|
+
/**
|
|
49204
|
+
* Builds the session rows so the renderer can keep one consistent structure without duplicating labels.
|
|
49205
|
+
*/
|
|
49206
|
+
function buildSessionRows(options, bodyWidth, pausePresentation) {
|
|
49207
|
+
const runnerParts = [options.config.agentName || 'No agent selected'];
|
|
49208
|
+
if (options.config.modelName) {
|
|
49209
|
+
runnerParts.push(options.config.modelName);
|
|
49210
|
+
}
|
|
49211
|
+
if (options.config.thinkingLevel) {
|
|
49212
|
+
runnerParts.push(`thinking ${options.config.thinkingLevel}`);
|
|
49213
|
+
}
|
|
49214
|
+
const configurationRows = [
|
|
49215
|
+
...buildOptionalSessionRow('Context', options.config.context),
|
|
49216
|
+
...buildOptionalSessionRow('Test', options.config.testCommand),
|
|
49217
|
+
];
|
|
49218
|
+
return [
|
|
49219
|
+
{
|
|
49220
|
+
label: 'State',
|
|
49221
|
+
value: `${pausePresentation.badge} ${pausePresentation.stateMessage}`,
|
|
49222
|
+
},
|
|
49223
|
+
{
|
|
49224
|
+
label: 'Runner',
|
|
49225
|
+
value: runnerParts.join(' · '),
|
|
49226
|
+
},
|
|
49227
|
+
...configurationRows,
|
|
49228
|
+
{
|
|
49229
|
+
label: 'This run',
|
|
49230
|
+
value: buildThisRunSummary(options.progress),
|
|
49231
|
+
},
|
|
49232
|
+
{
|
|
49233
|
+
label: 'Backlog',
|
|
49234
|
+
value: buildBacklogSummary(options.progress),
|
|
49235
|
+
},
|
|
49236
|
+
{
|
|
49237
|
+
label: 'Scope',
|
|
49238
|
+
value: buildScopeSummary(options.progress, options.config),
|
|
49239
|
+
},
|
|
49240
|
+
{
|
|
49241
|
+
label: 'Timing',
|
|
49242
|
+
value: buildTimingSummary(options.progress),
|
|
49243
|
+
},
|
|
49244
|
+
{
|
|
49245
|
+
label: 'Progress',
|
|
49246
|
+
value: buildProgressBar(options.progress.percentage, bodyWidth - SESSION_LABEL_WIDTH - 1, `${options.progress.percentage}% complete (${options.progress.sessionDone}/${options.progress.sessionTotal} done)`),
|
|
49247
|
+
},
|
|
49248
|
+
];
|
|
49249
|
+
}
|
|
48808
49250
|
/**
|
|
48809
49251
|
* Builds the fixed-height live output section so streaming updates do not keep resizing the frame.
|
|
48810
49252
|
*/
|
|
48811
49253
|
function buildVisibleOutputLines(agentOutputLines) {
|
|
48812
49254
|
const visibleOutputLines = agentOutputLines.length > 0
|
|
48813
49255
|
? agentOutputLines.slice(-MAX_VISIBLE_OUTPUT_LINES).map((line) => `› ${stripAnsi(line)}`)
|
|
48814
|
-
: ['No live agent output yet.'];
|
|
49256
|
+
: [colors__default["default"].gray('No live agent output yet.')];
|
|
48815
49257
|
while (visibleOutputLines.length < MAX_VISIBLE_OUTPUT_LINES) {
|
|
48816
49258
|
visibleOutputLines.push('');
|
|
48817
49259
|
}
|
|
@@ -48834,25 +49276,84 @@ bash "$1"
|
|
|
48834
49276
|
return [topBorder, ...body, bottomBorder];
|
|
48835
49277
|
}
|
|
48836
49278
|
/**
|
|
48837
|
-
* Builds
|
|
49279
|
+
* Builds one aligned labeled line inside the session box.
|
|
48838
49280
|
*/
|
|
48839
|
-
function
|
|
48840
|
-
const
|
|
48841
|
-
|
|
48842
|
-
|
|
49281
|
+
function buildLabeledSessionLine(label, value, bodyWidth) {
|
|
49282
|
+
const formattedLabel = colors__default["default"].gray(label.padEnd(SESSION_LABEL_WIDTH));
|
|
49283
|
+
return `${formattedLabel} ${fitAnsiText(value, bodyWidth - SESSION_LABEL_WIDTH - 1)}`;
|
|
49284
|
+
}
|
|
49285
|
+
/**
|
|
49286
|
+
* Builds zero or one structured session row for optional metadata.
|
|
49287
|
+
*/
|
|
49288
|
+
function buildOptionalSessionRow(label, value) {
|
|
49289
|
+
if (!value) {
|
|
49290
|
+
return [];
|
|
49291
|
+
}
|
|
49292
|
+
return [{ label, value }];
|
|
49293
|
+
}
|
|
49294
|
+
/**
|
|
49295
|
+
* Builds the active-session summary shown in the session box.
|
|
49296
|
+
*/
|
|
49297
|
+
function buildThisRunSummary(progress) {
|
|
49298
|
+
if (progress.sessionTotal === 0) {
|
|
49299
|
+
return 'No runnable prompts in current scope';
|
|
49300
|
+
}
|
|
49301
|
+
return `Task ${progress.currentPromptIndex}/${progress.sessionTotal} · ${progress.sessionDone} done · ${progress.sessionRemaining} left`;
|
|
49302
|
+
}
|
|
49303
|
+
/**
|
|
49304
|
+
* Builds the backlog/filter summary shown in the session box.
|
|
49305
|
+
*/
|
|
49306
|
+
function buildBacklogSummary(progress) {
|
|
49307
|
+
const parts = [`Repo ${progress.totalPrompts} total`];
|
|
49308
|
+
if (progress.skippedPrompts > 0) {
|
|
49309
|
+
parts.push(`${formatPromptCount(progress.skippedPrompts)} below priority`);
|
|
48843
49310
|
}
|
|
48844
|
-
|
|
48845
|
-
|
|
49311
|
+
return parts.join(' · ');
|
|
49312
|
+
}
|
|
49313
|
+
/**
|
|
49314
|
+
* Builds the priority/write-order summary shown in the session box.
|
|
49315
|
+
*/
|
|
49316
|
+
function buildScopeSummary(progress, config) {
|
|
49317
|
+
const parts = [`Priority ≥${config.priority}`];
|
|
49318
|
+
if (progress.toBeWrittenPrompts > 0) {
|
|
49319
|
+
parts.push(`Write ${formatPromptCount(progress.toBeWrittenPrompts)} first`);
|
|
48846
49320
|
}
|
|
48847
49321
|
return parts.join(' · ');
|
|
48848
49322
|
}
|
|
49323
|
+
/**
|
|
49324
|
+
* Builds the elapsed/estimate summary shown in the session box.
|
|
49325
|
+
*/
|
|
49326
|
+
function buildTimingSummary(progress) {
|
|
49327
|
+
return `Elapsed ${progress.elapsedText} · Total ${progress.estimatedTotalText} · ETA ${progress.estimatedLabel}`;
|
|
49328
|
+
}
|
|
48849
49329
|
/**
|
|
48850
49330
|
* Builds the colored phase badge shown in the session box.
|
|
48851
49331
|
*/
|
|
48852
|
-
function
|
|
48853
|
-
if (pauseState
|
|
48854
|
-
return
|
|
49332
|
+
function buildPausePresentation(phase, pauseState, statusMessage) {
|
|
49333
|
+
if (pauseState === 'PAUSING') {
|
|
49334
|
+
return {
|
|
49335
|
+
badge: colors__default["default"].bgYellow.black(' PAUSING '),
|
|
49336
|
+
stateMessage: 'Pausing before the next task',
|
|
49337
|
+
pauseControl: colors__default["default"].bgMagenta.white(' P ') + colors__default["default"].white(' Cancel pause'),
|
|
49338
|
+
};
|
|
48855
49339
|
}
|
|
49340
|
+
if (pauseState === 'PAUSED') {
|
|
49341
|
+
return {
|
|
49342
|
+
badge: colors__default["default"].bgWhite.black(' PAUSED '),
|
|
49343
|
+
stateMessage: 'Paused until resumed',
|
|
49344
|
+
pauseControl: colors__default["default"].bgGreen.black(' P ') + colors__default["default"].white(' Resume'),
|
|
49345
|
+
};
|
|
49346
|
+
}
|
|
49347
|
+
return {
|
|
49348
|
+
badge: buildRunningPhaseBadge(phase),
|
|
49349
|
+
stateMessage: statusMessage,
|
|
49350
|
+
pauseControl: colors__default["default"].bgYellow.black(' P ') + colors__default["default"].white(' Pause'),
|
|
49351
|
+
};
|
|
49352
|
+
}
|
|
49353
|
+
/**
|
|
49354
|
+
* Builds the active phase badge shown in the session box while the runner is not paused.
|
|
49355
|
+
*/
|
|
49356
|
+
function buildRunningPhaseBadge(phase) {
|
|
48856
49357
|
switch (phase) {
|
|
48857
49358
|
case 'loading':
|
|
48858
49359
|
case 'initializing':
|
|
@@ -48867,6 +49368,8 @@ bash "$1"
|
|
|
48867
49368
|
return colors__default["default"].bgGreen.black(' DONE ');
|
|
48868
49369
|
case 'error':
|
|
48869
49370
|
return colors__default["default"].bgRed.white(' ERROR ');
|
|
49371
|
+
case 'paused':
|
|
49372
|
+
return colors__default["default"].bgWhite.black(' READY ');
|
|
48870
49373
|
default:
|
|
48871
49374
|
return colors__default["default"].bgWhite.black(' READY ');
|
|
48872
49375
|
}
|
|
@@ -48879,7 +49382,7 @@ bash "$1"
|
|
|
48879
49382
|
const barWidth = Math.max(10, availableWidth - percentageLabel.length - 1);
|
|
48880
49383
|
const filledWidth = Math.round((percentage / 100) * barWidth);
|
|
48881
49384
|
const emptyWidth = Math.max(0, barWidth - filledWidth);
|
|
48882
|
-
return `${colors__default["default"].green('█'.repeat(filledWidth))}${colors__default["default"].
|
|
49385
|
+
return `${colors__default["default"].green('█'.repeat(filledWidth))}${colors__default["default"].blue('░'.repeat(emptyWidth))} ${percentageLabel}`;
|
|
48883
49386
|
}
|
|
48884
49387
|
/**
|
|
48885
49388
|
* Formats a prompt count with singular/plural wording.
|
|
@@ -48890,58 +49393,15 @@ bash "$1"
|
|
|
48890
49393
|
/**
|
|
48891
49394
|
* Builds the control pills shown in the footer box.
|
|
48892
49395
|
*/
|
|
48893
|
-
function buildControlPills(
|
|
49396
|
+
function buildControlPills(pauseControl, pendingEnterLabel) {
|
|
48894
49397
|
const pills = [];
|
|
48895
49398
|
if (pendingEnterLabel) {
|
|
48896
49399
|
pills.push(colors__default["default"].bgWhite.black(' ENTER ') + colors__default["default"].white(` ${pendingEnterLabel}`));
|
|
48897
49400
|
}
|
|
48898
|
-
pills.push(
|
|
48899
|
-
? colors__default["default"].bgYellow.black(' P ') + colors__default["default"].white(' Pause')
|
|
48900
|
-
: colors__default["default"].bgYellow.black(' P ') + colors__default["default"].white(' Resume'));
|
|
49401
|
+
pills.push(pauseControl);
|
|
48901
49402
|
pills.push(colors__default["default"].bgRed.white(' CTRL+C ') + colors__default["default"].white(' Exit'));
|
|
48902
49403
|
return pills;
|
|
48903
49404
|
}
|
|
48904
|
-
/**
|
|
48905
|
-
* Pads or truncates a possibly ANSI-colored line to the target visible width.
|
|
48906
|
-
*/
|
|
48907
|
-
function padAnsiText(text, width) {
|
|
48908
|
-
const fittedText = fitAnsiText(text, width);
|
|
48909
|
-
return fittedText + ' '.repeat(Math.max(0, width - visibleLength(fittedText)));
|
|
48910
|
-
}
|
|
48911
|
-
/**
|
|
48912
|
-
* Truncates a possibly ANSI-colored line to the target visible width.
|
|
48913
|
-
*/
|
|
48914
|
-
function fitAnsiText(text, width) {
|
|
48915
|
-
if (visibleLength(text) <= width) {
|
|
48916
|
-
return text;
|
|
48917
|
-
}
|
|
48918
|
-
return fitPlainText(stripAnsi(text), width);
|
|
48919
|
-
}
|
|
48920
|
-
/**
|
|
48921
|
-
* Truncates a plain-text line to the target width with an ellipsis.
|
|
48922
|
-
*/
|
|
48923
|
-
function fitPlainText(text, width) {
|
|
48924
|
-
if (text.length <= width) {
|
|
48925
|
-
return text;
|
|
48926
|
-
}
|
|
48927
|
-
if (width <= 3) {
|
|
48928
|
-
return '.'.repeat(width);
|
|
48929
|
-
}
|
|
48930
|
-
return `${text.slice(0, width - 3)}...`;
|
|
48931
|
-
}
|
|
48932
|
-
/**
|
|
48933
|
-
* Measures visible string width by stripping ANSI escape codes.
|
|
48934
|
-
*/
|
|
48935
|
-
function visibleLength(text) {
|
|
48936
|
-
return stripAnsi(text).length;
|
|
48937
|
-
}
|
|
48938
|
-
/**
|
|
48939
|
-
* Strips ANSI escape codes from a string.
|
|
48940
|
-
*/
|
|
48941
|
-
function stripAnsi(text) {
|
|
48942
|
-
// eslint-disable-next-line no-control-regex
|
|
48943
|
-
return text.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, '');
|
|
48944
|
-
}
|
|
48945
49405
|
|
|
48946
49406
|
/**
|
|
48947
49407
|
* Maximum number of agent output lines kept in the scrolling output area.
|
|
@@ -49082,34 +49542,6 @@ bash "$1"
|
|
|
49082
49542
|
}
|
|
49083
49543
|
}
|
|
49084
49544
|
|
|
49085
|
-
/**
|
|
49086
|
-
* Refresh cadence used only while the rich coder UI needs animated updates.
|
|
49087
|
-
*
|
|
49088
|
-
* @private internal constant of coder run UI
|
|
49089
|
-
*/
|
|
49090
|
-
const ACTIVE_CODER_RUN_UI_REFRESH_INTERVAL_MS = 1000;
|
|
49091
|
-
/**
|
|
49092
|
-
* Phases that still benefit from automatic refreshes because the frame can change
|
|
49093
|
-
* over time even without new runner output.
|
|
49094
|
-
*
|
|
49095
|
-
* @private internal constant of coder run UI
|
|
49096
|
-
*/
|
|
49097
|
-
const AUTO_REFRESH_PHASES = ['initializing', 'loading', 'running', 'verifying'];
|
|
49098
|
-
/**
|
|
49099
|
-
* Returns the automatic refresh interval for the current UI state.
|
|
49100
|
-
*
|
|
49101
|
-
* Waiting, paused, and completed states return `undefined` so the rich UI stays
|
|
49102
|
-
* perfectly still until actual state changes arrive.
|
|
49103
|
-
*
|
|
49104
|
-
* @private internal utility of coder run UI
|
|
49105
|
-
*/
|
|
49106
|
-
function getCoderRunUiAutoRefreshInterval(phase, pauseState) {
|
|
49107
|
-
if (pauseState !== 'RUNNING') {
|
|
49108
|
-
return undefined;
|
|
49109
|
-
}
|
|
49110
|
-
return AUTO_REFRESH_PHASES.includes(phase) ? ACTIVE_CODER_RUN_UI_REFRESH_INTERVAL_MS : undefined;
|
|
49111
|
-
}
|
|
49112
|
-
|
|
49113
49545
|
/**
|
|
49114
49546
|
* Spinner animation frames.
|
|
49115
49547
|
*
|
|
@@ -49288,6 +49720,7 @@ bash "$1"
|
|
|
49288
49720
|
function buildFrameLines() {
|
|
49289
49721
|
return buildCoderRunUiFrame({
|
|
49290
49722
|
terminalWidth: getTerminalWidth(),
|
|
49723
|
+
animationFrame: spinnerFrame,
|
|
49291
49724
|
spinner: SPINNER_FRAMES[spinnerFrame],
|
|
49292
49725
|
pauseState: getPauseState(),
|
|
49293
49726
|
config: state.config,
|
|
@@ -49335,12 +49768,7 @@ bash "$1"
|
|
|
49335
49768
|
process.exit(0);
|
|
49336
49769
|
}
|
|
49337
49770
|
if (key.name === 'p') {
|
|
49338
|
-
|
|
49339
|
-
requestPause();
|
|
49340
|
-
}
|
|
49341
|
-
else {
|
|
49342
|
-
requestResume();
|
|
49343
|
-
}
|
|
49771
|
+
togglePauseState();
|
|
49344
49772
|
scheduleRender();
|
|
49345
49773
|
return;
|
|
49346
49774
|
}
|
|
@@ -49476,6 +49904,23 @@ bash "$1"
|
|
|
49476
49904
|
const isRichUiEnabled = !options.dryRun && !options.noUi && Boolean(process.stdout.isTTY);
|
|
49477
49905
|
const progressDisplay = options.dryRun || options.noUi || isRichUiEnabled ? undefined : new CliProgressDisplay(runStartDate, options.priority);
|
|
49478
49906
|
const uiHandle = isRichUiEnabled ? renderCoderRunUi(runStartDate) : undefined;
|
|
49907
|
+
const waitForRequestedPause = async () => {
|
|
49908
|
+
await checkPause({
|
|
49909
|
+
silent: isRichUiEnabled,
|
|
49910
|
+
onPaused: () => {
|
|
49911
|
+
progressDisplay === null || progressDisplay === void 0 ? void 0 : progressDisplay.pauseTimer();
|
|
49912
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.pauseTimer();
|
|
49913
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('paused');
|
|
49914
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage('Paused');
|
|
49915
|
+
},
|
|
49916
|
+
onResumed: () => {
|
|
49917
|
+
progressDisplay === null || progressDisplay === void 0 ? void 0 : progressDisplay.resumeTimer();
|
|
49918
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.resumeTimer();
|
|
49919
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('loading');
|
|
49920
|
+
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage('Resuming...');
|
|
49921
|
+
},
|
|
49922
|
+
});
|
|
49923
|
+
};
|
|
49479
49924
|
// When the Ink UI is active it handles keyboard input itself, so skip the raw stdin listener.
|
|
49480
49925
|
if (!isRichUiEnabled) {
|
|
49481
49926
|
listenForPause();
|
|
@@ -49592,19 +50037,7 @@ bash "$1"
|
|
|
49592
50037
|
let hasShownUpcomingTasks = false;
|
|
49593
50038
|
let hasWaitedForStart = false;
|
|
49594
50039
|
while (just(true)) {
|
|
49595
|
-
await
|
|
49596
|
-
silent: isRichUiEnabled,
|
|
49597
|
-
onPaused: () => {
|
|
49598
|
-
progressDisplay === null || progressDisplay === void 0 ? void 0 : progressDisplay.pauseTimer();
|
|
49599
|
-
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.pauseTimer();
|
|
49600
|
-
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('paused');
|
|
49601
|
-
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage('Paused');
|
|
49602
|
-
},
|
|
49603
|
-
onResumed: () => {
|
|
49604
|
-
progressDisplay === null || progressDisplay === void 0 ? void 0 : progressDisplay.resumeTimer();
|
|
49605
|
-
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.resumeTimer();
|
|
49606
|
-
},
|
|
49607
|
-
});
|
|
50040
|
+
await waitForRequestedPause();
|
|
49608
50041
|
if (isRichUiEnabled) {
|
|
49609
50042
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('loading');
|
|
49610
50043
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setStatusMessage('Loading prompts...');
|
|
@@ -49672,6 +50105,7 @@ bash "$1"
|
|
|
49672
50105
|
const commitMessage = buildCommitMessage(nextPrompt.file, nextPrompt.section);
|
|
49673
50106
|
const codexPrompt = appendCoderContext(buildCodexPrompt(nextPrompt.file, nextPrompt.section), resolvedCoderContext);
|
|
49674
50107
|
const scriptPath = buildScriptPath(nextPrompt.file, nextPrompt.section);
|
|
50108
|
+
await waitForRequestedPause();
|
|
49675
50109
|
if (isRichUiEnabled) {
|
|
49676
50110
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setCurrentPrompt(promptLabel);
|
|
49677
50111
|
uiHandle === null || uiHandle === void 0 ? void 0 : uiHandle.state.setPhase('running');
|
|
@@ -53736,9 +54170,10 @@ bash "$1"
|
|
|
53736
54170
|
* Build a minimal agent source snapshot for remote agents.
|
|
53737
54171
|
*/
|
|
53738
54172
|
function buildRemoteAgentSource(profile, meta) {
|
|
54173
|
+
const isMetaImageExplicit = profile.isMetaImageExplicit !== false;
|
|
53739
54174
|
const metaLines = [
|
|
53740
54175
|
formatMetaLine('FULLNAME', meta === null || meta === void 0 ? void 0 : meta.fullname),
|
|
53741
|
-
formatMetaLine('IMAGE', meta === null || meta === void 0 ? void 0 : meta.image),
|
|
54176
|
+
formatMetaLine('IMAGE', isMetaImageExplicit ? meta === null || meta === void 0 ? void 0 : meta.image : undefined),
|
|
53742
54177
|
formatMetaLine('DESCRIPTION', meta === null || meta === void 0 ? void 0 : meta.description),
|
|
53743
54178
|
formatMetaLine('COLOR', meta === null || meta === void 0 ? void 0 : meta.color),
|
|
53744
54179
|
formatMetaLine('FONT', meta === null || meta === void 0 ? void 0 : meta.font),
|
|
@@ -53837,6 +54272,8 @@ bash "$1"
|
|
|
53837
54272
|
remoteAgent._isVoiceCallingEnabled = profile.isVoiceCallingEnabled === true; // [✨✷] Store voice calling status
|
|
53838
54273
|
remoteAgent._isVoiceTtsSttEnabled = profile.isVoiceTtsSttEnabled !== false;
|
|
53839
54274
|
remoteAgent.knowledgeSources = profile.knowledgeSources || [];
|
|
54275
|
+
remoteAgent.isMetaImageExplicit = profile.isMetaImageExplicit !== false;
|
|
54276
|
+
remoteAgent.avatarVisualId = profile.avatarVisualId;
|
|
53840
54277
|
return remoteAgent;
|
|
53841
54278
|
}
|
|
53842
54279
|
/**
|
|
@@ -53852,6 +54289,8 @@ bash "$1"
|
|
|
53852
54289
|
this.toolTitles = {};
|
|
53853
54290
|
this._isVoiceCallingEnabled = false; // [✨✷] Track voice calling status
|
|
53854
54291
|
this._isVoiceTtsSttEnabled = true;
|
|
54292
|
+
this.isMetaImageExplicit = true;
|
|
54293
|
+
this.avatarVisualId = undefined;
|
|
53855
54294
|
this.knowledgeSources = [];
|
|
53856
54295
|
this.agentUrl = options.agentUrl;
|
|
53857
54296
|
}
|