opkg 0.9.2 → 0.9.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/package.json +3 -1
- package/packages/cli/dist/add-U44SL3OR.js +624 -0
- package/packages/cli/dist/add-U44SL3OR.js.map +7 -0
- package/packages/cli/dist/chunk-4B5HJLP2.js +48 -0
- package/packages/cli/dist/chunk-4B5HJLP2.js.map +7 -0
- package/packages/cli/dist/chunk-BROJ6OUT.js +631 -0
- package/packages/cli/dist/chunk-BROJ6OUT.js.map +7 -0
- package/packages/cli/dist/chunk-CVA64SXK.js +1136 -0
- package/packages/cli/dist/chunk-CVA64SXK.js.map +7 -0
- package/packages/cli/dist/chunk-D6LEPODL.js +413 -0
- package/packages/cli/dist/chunk-D6LEPODL.js.map +7 -0
- package/packages/cli/dist/chunk-HTYHJA3B.js +61 -0
- package/packages/cli/dist/chunk-HTYHJA3B.js.map +7 -0
- package/packages/cli/dist/chunk-KI7FDU3H.js +99 -0
- package/packages/cli/dist/chunk-KI7FDU3H.js.map +7 -0
- package/packages/cli/dist/chunk-PSQXKAL4.js +371 -0
- package/packages/cli/dist/chunk-PSQXKAL4.js.map +7 -0
- package/packages/cli/dist/chunk-PUDRKDVZ.js +1419 -0
- package/packages/cli/dist/chunk-PUDRKDVZ.js.map +7 -0
- package/packages/cli/dist/chunk-U7FW7SXX.js +568 -0
- package/packages/cli/dist/chunk-U7FW7SXX.js.map +7 -0
- package/packages/cli/dist/configure-3AZUMDJZ.js +107 -0
- package/packages/cli/dist/configure-3AZUMDJZ.js.map +7 -0
- package/packages/cli/dist/index.js +15 -15
- package/packages/cli/dist/install-EZNWMLJR.js +7581 -0
- package/packages/cli/dist/install-EZNWMLJR.js.map +7 -0
- package/packages/cli/dist/list-XR7RSJFS.js +327 -0
- package/packages/cli/dist/list-XR7RSJFS.js.map +7 -0
- package/packages/cli/dist/login-NRKHXZKM.js +150 -0
- package/packages/cli/dist/login-NRKHXZKM.js.map +7 -0
- package/packages/cli/dist/logout-SYHXCVCQ.js +40 -0
- package/packages/cli/dist/logout-SYHXCVCQ.js.map +7 -0
- package/packages/cli/dist/new-F46OSD72.js +277 -0
- package/packages/cli/dist/new-F46OSD72.js.map +7 -0
- package/packages/cli/dist/publish-4H43PCSG.js +619 -0
- package/packages/cli/dist/publish-4H43PCSG.js.map +7 -0
- package/packages/cli/dist/remove-BD52BHR2.js +542 -0
- package/packages/cli/dist/remove-BD52BHR2.js.map +7 -0
- package/packages/cli/dist/save-N3QWF2WN.js +1728 -0
- package/packages/cli/dist/save-N3QWF2WN.js.map +7 -0
- package/packages/cli/dist/search-ABROK3UO.js +157 -0
- package/packages/cli/dist/search-ABROK3UO.js.map +7 -0
- package/packages/cli/dist/set-NGM2FIKF.js +251 -0
- package/packages/cli/dist/set-NGM2FIKF.js.map +7 -0
- package/packages/cli/dist/uninstall-Q3CP4UN5.js +539 -0
- package/packages/cli/dist/uninstall-Q3CP4UN5.js.map +7 -0
- package/packages/cli/dist/unpublish-VBTNTMS5.js +245 -0
- package/packages/cli/dist/unpublish-VBTNTMS5.js.map +7 -0
- package/packages/cli/dist/view-MXRBMXOG.js +488 -0
- package/packages/cli/dist/view-MXRBMXOG.js.map +7 -0
- package/packages/cli/package.json +2 -0
- package/packages/core/dist/core/install/conflicts/file-conflict-resolver.d.ts +5 -4
- package/packages/core/dist/core/install/conflicts/file-conflict-resolver.d.ts.map +1 -1
- package/packages/core/dist/core/install/conflicts/file-conflict-resolver.js +6 -5
- package/packages/core/dist/core/install/conflicts/file-conflict-resolver.js.map +1 -1
- package/packages/core/dist/core/install/install-reporting.js +1 -1
- package/packages/core/dist/core/install/install-reporting.js.map +1 -1
- package/packages/core/dist/core/install/list-handler.d.ts.map +1 -1
- package/packages/core/dist/core/install/list-handler.js +7 -0
- package/packages/core/dist/core/install/list-handler.js.map +1 -1
- package/packages/core/dist/core/install/marketplace-handler.d.ts.map +1 -1
- package/packages/core/dist/core/install/marketplace-handler.js.map +1 -1
- package/packages/core/dist/core/install/operations/conflict-handler.d.ts +2 -1
- package/packages/core/dist/core/install/operations/conflict-handler.d.ts.map +1 -1
- package/packages/core/dist/core/install/operations/conflict-handler.js +2 -2
- package/packages/core/dist/core/install/operations/conflict-handler.js.map +1 -1
- package/packages/core/dist/core/install/orchestrator/orchestrator.d.ts.map +1 -1
- package/packages/core/dist/core/install/orchestrator/orchestrator.js +22 -14
- package/packages/core/dist/core/install/orchestrator/orchestrator.js.map +1 -1
- package/packages/core/dist/core/install/orchestrator/strategies/git-strategy.d.ts +1 -0
- package/packages/core/dist/core/install/orchestrator/strategies/git-strategy.d.ts.map +1 -1
- package/packages/core/dist/core/install/orchestrator/strategies/git-strategy.js +11 -24
- package/packages/core/dist/core/install/orchestrator/strategies/git-strategy.js.map +1 -1
- package/packages/core/dist/core/install/orchestrator/strategies/path-strategy.d.ts +2 -0
- package/packages/core/dist/core/install/orchestrator/strategies/path-strategy.d.ts.map +1 -1
- package/packages/core/dist/core/install/orchestrator/strategies/path-strategy.js +14 -14
- package/packages/core/dist/core/install/orchestrator/strategies/path-strategy.js.map +1 -1
- package/packages/core/dist/core/install/orchestrator/strategies/registry-strategy.d.ts +7 -0
- package/packages/core/dist/core/install/orchestrator/strategies/registry-strategy.d.ts.map +1 -1
- package/packages/core/dist/core/install/orchestrator/strategies/registry-strategy.js +28 -0
- package/packages/core/dist/core/install/orchestrator/strategies/registry-strategy.js.map +1 -1
- package/packages/core/dist/core/install/platform-resolution.d.ts +3 -0
- package/packages/core/dist/core/install/platform-resolution.d.ts.map +1 -1
- package/packages/core/dist/core/install/platform-resolution.js +5 -2
- package/packages/core/dist/core/install/platform-resolution.js.map +1 -1
- package/packages/core/dist/core/install/preprocessing/context-population.d.ts +18 -0
- package/packages/core/dist/core/install/preprocessing/context-population.d.ts.map +1 -0
- package/packages/core/dist/core/install/preprocessing/context-population.js +36 -0
- package/packages/core/dist/core/install/preprocessing/context-population.js.map +1 -0
- package/packages/core/dist/core/install/preprocessing/convenience-preprocessor.d.ts +23 -0
- package/packages/core/dist/core/install/preprocessing/convenience-preprocessor.d.ts.map +1 -1
- package/packages/core/dist/core/install/preprocessing/convenience-preprocessor.js +44 -0
- package/packages/core/dist/core/install/preprocessing/convenience-preprocessor.js.map +1 -1
- package/packages/core/dist/core/install/sources/path-source.d.ts.map +1 -1
- package/packages/core/dist/core/install/sources/path-source.js +8 -0
- package/packages/core/dist/core/install/sources/path-source.js.map +1 -1
- package/packages/core/dist/core/install/unified/context-builders.d.ts +5 -0
- package/packages/core/dist/core/install/unified/context-builders.d.ts.map +1 -1
- package/packages/core/dist/core/install/unified/context-builders.js +13 -0
- package/packages/core/dist/core/install/unified/context-builders.js.map +1 -1
- package/packages/core/dist/core/install/unified/context-helpers.d.ts.map +1 -1
- package/packages/core/dist/core/install/unified/context-helpers.js +5 -0
- package/packages/core/dist/core/install/unified/context-helpers.js.map +1 -1
- package/packages/core/dist/core/install/unified/context.d.ts +6 -0
- package/packages/core/dist/core/install/unified/context.d.ts.map +1 -1
- package/packages/core/dist/core/install/unified/multi-context-pipeline.d.ts.map +1 -1
- package/packages/core/dist/core/install/unified/multi-context-pipeline.js +0 -2
- package/packages/core/dist/core/install/unified/multi-context-pipeline.js.map +1 -1
- package/packages/core/dist/core/install/unified/phases/conflicts.d.ts.map +1 -1
- package/packages/core/dist/core/install/unified/phases/conflicts.js +1 -1
- package/packages/core/dist/core/install/unified/phases/conflicts.js.map +1 -1
- package/packages/core/dist/core/install/unified/phases/execute.d.ts.map +1 -1
- package/packages/core/dist/core/install/unified/phases/execute.js +2 -1
- package/packages/core/dist/core/install/unified/phases/execute.js.map +1 -1
- package/packages/core/dist/core/install/unified/phases/report.js +1 -1
- package/packages/core/dist/core/install/unified/phases/report.js.map +1 -1
- package/packages/core/dist/core/install/unified/pipeline.d.ts.map +1 -1
- package/packages/core/dist/core/install/unified/pipeline.js +5 -2
- package/packages/core/dist/core/install/unified/pipeline.js.map +1 -1
- package/packages/core/dist/core/ports/resolve.d.ts +0 -13
- package/packages/core/dist/core/ports/resolve.d.ts.map +1 -1
- package/packages/core/dist/core/ports/resolve.js +0 -28
- package/packages/core/dist/core/ports/resolve.js.map +1 -1
- package/packages/core/dist/core/remove/removal-confirmation.d.ts +4 -1
- package/packages/core/dist/core/remove/removal-confirmation.d.ts.map +1 -1
- package/packages/core/dist/core/remove/removal-confirmation.js +5 -4
- package/packages/core/dist/core/remove/removal-confirmation.js.map +1 -1
- package/packages/core/dist/core/remove/remove-from-source-pipeline.d.ts.map +1 -1
- package/packages/core/dist/core/remove/remove-from-source-pipeline.js +1 -10
- package/packages/core/dist/core/remove/remove-from-source-pipeline.js.map +1 -1
- package/packages/core/dist/core/uninstall/uninstall-executor.js +1 -1
- package/packages/core/dist/core/uninstall/uninstall-executor.js.map +1 -1
- package/packages/core/dist/core/uninstall/uninstall-reporter.d.ts +2 -2
- package/packages/core/dist/core/uninstall/uninstall-reporter.d.ts.map +1 -1
- package/packages/core/dist/core/uninstall/uninstall-reporter.js +4 -4
- package/packages/core/dist/core/uninstall/uninstall-reporter.js.map +1 -1
- package/packages/core/dist/index.d.ts +1 -1
- package/packages/core/dist/index.d.ts.map +1 -1
- package/packages/core/dist/types/execution-context.d.ts +24 -10
- package/packages/core/dist/types/execution-context.d.ts.map +1 -1
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
configManager
|
|
4
|
+
} from "./chunk-S6OARUVQ.js";
|
|
5
|
+
import {
|
|
6
|
+
resolvePrompt
|
|
7
|
+
} from "./chunk-BROJ6OUT.js";
|
|
8
|
+
import {
|
|
9
|
+
getPackagePath
|
|
10
|
+
} from "./chunk-XEPVYZO3.js";
|
|
11
|
+
import {
|
|
12
|
+
isScopedName,
|
|
13
|
+
normalizePackageName,
|
|
14
|
+
validatePackageName
|
|
15
|
+
} from "./chunk-VN22A7NW.js";
|
|
16
|
+
import {
|
|
17
|
+
exists
|
|
18
|
+
} from "./chunk-S47F4OG4.js";
|
|
19
|
+
import {
|
|
20
|
+
UserCancellationError
|
|
21
|
+
} from "./chunk-ID4SVDQZ.js";
|
|
22
|
+
|
|
23
|
+
// ../core/src/core/scoping/package-scoping.ts
|
|
24
|
+
async function isPackageNameTaken(name) {
|
|
25
|
+
let normalized = normalizePackageName(name);
|
|
26
|
+
return await exists(getPackagePath(normalized));
|
|
27
|
+
}
|
|
28
|
+
function buildScopedNameFromScope(unscopedName, scope) {
|
|
29
|
+
let normalizedScope = normalizePackageName(scope.replace(/^@/, "")), normalizedName = normalizePackageName(unscopedName);
|
|
30
|
+
return `@${normalizedScope}/${normalizedName}`;
|
|
31
|
+
}
|
|
32
|
+
async function ensureScopedNameAvailable(name) {
|
|
33
|
+
try {
|
|
34
|
+
validatePackageName(name);
|
|
35
|
+
} catch (error) {
|
|
36
|
+
throw new Error(error.message.replace("%s", name));
|
|
37
|
+
}
|
|
38
|
+
if (!isScopedName(name))
|
|
39
|
+
throw new Error("Name must be scoped (e.g. @scope/name)");
|
|
40
|
+
if (await isPackageNameTaken(name))
|
|
41
|
+
throw new Error(
|
|
42
|
+
`Package '${name}' already exists in local registry. Choose a different scoped name.`
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
async function getDefaultScopeForProfile(profileName) {
|
|
46
|
+
return profileName ? (await configManager.getAll()).profiles?.[profileName]?.defaults?.scope : void 0;
|
|
47
|
+
}
|
|
48
|
+
async function resolveScopedNameForPushWithUserScope(unscopedName, username, profileName, prompt) {
|
|
49
|
+
if (isScopedName(unscopedName))
|
|
50
|
+
throw new Error(`Expected unscoped name, received '${unscopedName}'`);
|
|
51
|
+
if (!username?.trim())
|
|
52
|
+
throw new Error("Username is required to apply default scope.");
|
|
53
|
+
let prm = prompt ?? resolvePrompt(), normalizedName = normalizePackageName(unscopedName), choice = await prm.select(
|
|
54
|
+
`Package '${normalizedName}' must be scoped before pushing. Choose a scope:`,
|
|
55
|
+
[
|
|
56
|
+
{
|
|
57
|
+
title: `Use default scope @${username}`,
|
|
58
|
+
value: "default",
|
|
59
|
+
description: `Renames to @${username}/${normalizedName}`
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
title: "Enter scope...",
|
|
63
|
+
value: "custom",
|
|
64
|
+
description: `Enter a custom scope for ${normalizedName}`
|
|
65
|
+
}
|
|
66
|
+
],
|
|
67
|
+
"Use arrow keys to select, Enter to confirm"
|
|
68
|
+
);
|
|
69
|
+
if (!choice)
|
|
70
|
+
throw new UserCancellationError("Operation cancelled by user");
|
|
71
|
+
let scope = username;
|
|
72
|
+
if (choice === "custom") {
|
|
73
|
+
let initialScope = (await getDefaultScopeForProfile(profileName))?.replace(/^@/, "") || username, enteredScope = await prm.text(
|
|
74
|
+
`Enter a scope (without @) for '${normalizedName}':`,
|
|
75
|
+
{
|
|
76
|
+
initial: initialScope,
|
|
77
|
+
validate: async (value) => {
|
|
78
|
+
if (!value) return "Scope is required";
|
|
79
|
+
let candidate = buildScopedNameFromScope(normalizedName, value);
|
|
80
|
+
try {
|
|
81
|
+
return await ensureScopedNameAvailable(candidate), !0;
|
|
82
|
+
} catch (error) {
|
|
83
|
+
return error.message;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
);
|
|
88
|
+
if (!enteredScope)
|
|
89
|
+
throw new UserCancellationError("Operation cancelled by user");
|
|
90
|
+
scope = enteredScope;
|
|
91
|
+
}
|
|
92
|
+
let scopedName = buildScopedNameFromScope(normalizedName, scope);
|
|
93
|
+
return await ensureScopedNameAvailable(scopedName), scopedName;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export {
|
|
97
|
+
resolveScopedNameForPushWithUserScope
|
|
98
|
+
};
|
|
99
|
+
//# sourceMappingURL=chunk-KI7FDU3H.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../core/src/core/scoping/package-scoping.ts"],
|
|
4
|
+
"sourcesContent": ["import { SCOPED_PACKAGE_REGEX, GITHUB_PACKAGE_REGEX, normalizePackageName, validatePackageName, isScopedName } from '../../utils/package-name.js';\n\nexport { isScopedName } from '../../utils/package-name.js';\nimport { listAllPackages, getPackagePath } from '../directory.js';\nimport { exists } from '../../utils/fs.js';\nimport { configManager } from '../config.js';\nimport type { PromptPort } from '../ports/prompt.js';\nimport { resolvePrompt } from '../ports/resolve.js';\nimport { UserCancellationError } from '../../utils/errors.js';\n\n/**\n * Extract the local (non-scope) part from a package name.\n */\nexport function getLocalPart(name: string): string {\n const match = name.match(SCOPED_PACKAGE_REGEX);\n return match ? match[2] : name;\n}\n\n/**\n * Get all scoped package names in the local registry that share the same local name.\n */\nconst PACKAGE_LIST_CACHE_TTL_MS = 5000;\nlet cachedPackageList: string[] | null = null;\nlet cachedPackageListTimestamp = 0;\n\nasync function getCachedPackageList(): Promise<string[]> {\n const now = Date.now();\n if (cachedPackageList && now - cachedPackageListTimestamp < PACKAGE_LIST_CACHE_TTL_MS) {\n return cachedPackageList;\n }\n\n cachedPackageList = await listAllPackages();\n cachedPackageListTimestamp = now;\n return cachedPackageList;\n}\n\nexport async function findScopedVariantsInRegistry(baseName: string): Promise<string[]> {\n const normalizedBase = normalizePackageName(baseName);\n const packages = await getCachedPackageList();\n\n return packages.filter(candidate => {\n const match = candidate.match(SCOPED_PACKAGE_REGEX);\n if (!match) {\n return false;\n }\n const localPart = normalizePackageName(match[2]);\n return localPart === normalizedBase;\n });\n}\n\nasync function isPackageNameTaken(name: string): Promise<boolean> {\n const normalized = normalizePackageName(name);\n return await exists(getPackagePath(normalized));\n}\n\nfunction buildScopedNameFromScope(unscopedName: string, scope: string): string {\n const normalizedScope = normalizePackageName(scope.replace(/^@/, ''));\n const normalizedName = normalizePackageName(unscopedName);\n return `@${normalizedScope}/${normalizedName}`;\n}\n\nasync function ensureScopedNameAvailable(name: string): Promise<void> {\n try {\n validatePackageName(name);\n } catch (error) {\n throw new Error((error as Error).message.replace('%s', name));\n }\n\n if (!isScopedName(name)) {\n throw new Error('Name must be scoped (e.g. @scope/name)');\n }\n\n if (await isPackageNameTaken(name)) {\n throw new Error(\n `Package '${name}' already exists in local registry. Choose a different scoped name.`\n );\n }\n}\n\n/**\n * Fetch the configured default scope for a given profile (if any).\n */\nexport async function getDefaultScopeForProfile(profileName?: string): Promise<string | undefined> {\n if (!profileName) {\n return undefined;\n }\n\n const config = await configManager.getAll();\n const profileConfig = config.profiles?.[profileName];\n return profileConfig?.defaults?.scope;\n}\n\n/**\n * Suggest a scoped package name using the configured default scope.\n */\nexport async function suggestScopedNameFromConfig(\n unscopedName: string,\n profileName?: string\n): Promise<string | undefined> {\n const defaultScope = await getDefaultScopeForProfile(profileName);\n if (!defaultScope) {\n return undefined;\n }\n\n const normalizedScope = normalizePackageName(defaultScope.replace(/^@/, ''));\n const normalizedName = normalizePackageName(unscopedName);\n return `@${normalizedScope}/${normalizedName}`;\n}\n\n/**\n * Prompt user for a new scoped name and ensure it does not already exist locally.\n */\nexport async function promptForNewScopedName(\n baseName: string,\n profileName?: string,\n message?: string,\n prompt?: PromptPort\n): Promise<string> {\n const prm = prompt ?? resolvePrompt();\n const initial = await suggestScopedNameFromConfig(baseName, profileName);\n\n const scopedName = await prm.text(\n message ?? `Enter a scoped name for '${baseName}' (format @scope/${baseName}):`,\n {\n initial,\n validate: async (value: string) => {\n if (!value) return 'Name is required';\n try {\n await ensureScopedNameAvailable(value);\n return true;\n } catch (error) {\n return (error as Error).message;\n }\n }\n }\n );\n\n if (!scopedName) {\n throw new UserCancellationError('Operation cancelled by user');\n }\n\n return normalizePackageName(scopedName);\n}\n\n/**\n * Determine the scoped name to use when pushing an unscoped package.\n */\nexport async function resolveScopedNameForPush(\n unscopedName: string,\n profileName?: string,\n prompt?: PromptPort\n): Promise<string> {\n if (isScopedName(unscopedName)) {\n throw new Error(`Expected unscoped name, received '${unscopedName}'`);\n }\n\n return await promptForNewScopedName(\n unscopedName,\n profileName,\n `Remote registry requires a scope. Enter a scoped name for '${unscopedName}' (format @scope/${unscopedName}):`,\n prompt\n );\n}\n\nexport async function resolveScopedNameForPushWithUserScope(\n unscopedName: string,\n username: string,\n profileName?: string,\n prompt?: PromptPort\n): Promise<string> {\n if (isScopedName(unscopedName)) {\n throw new Error(`Expected unscoped name, received '${unscopedName}'`);\n }\n\n if (!username?.trim()) {\n throw new Error('Username is required to apply default scope.');\n }\n\n const prm = prompt ?? resolvePrompt();\n const normalizedName = normalizePackageName(unscopedName);\n\n const choice = await prm.select<'default' | 'custom'>(\n `Package '${normalizedName}' must be scoped before pushing. Choose a scope:`,\n [\n {\n title: `Use default scope @${username}`,\n value: 'default' as const,\n description: `Renames to @${username}/${normalizedName}`\n },\n {\n title: 'Enter scope...',\n value: 'custom' as const,\n description: `Enter a custom scope for ${normalizedName}`\n }\n ],\n 'Use arrow keys to select, Enter to confirm'\n );\n\n if (!choice) {\n throw new UserCancellationError('Operation cancelled by user');\n }\n\n let scope = username;\n if (choice === 'custom') {\n const profileScope = await getDefaultScopeForProfile(profileName);\n const initialScope = profileScope?.replace(/^@/, '') || username;\n\n const enteredScope = await prm.text(\n `Enter a scope (without @) for '${normalizedName}':`,\n {\n initial: initialScope,\n validate: async (value: string) => {\n if (!value) return 'Scope is required';\n\n const candidate = buildScopedNameFromScope(normalizedName, value);\n try {\n await ensureScopedNameAvailable(candidate);\n return true;\n } catch (error) {\n return (error as Error).message;\n }\n }\n }\n );\n\n if (!enteredScope) {\n throw new UserCancellationError('Operation cancelled by user');\n }\n\n scope = enteredScope;\n }\n\n const scopedName = buildScopedNameFromScope(normalizedName, scope);\n await ensureScopedNameAvailable(scopedName);\n return scopedName;\n}\n\nexport interface SaveNameResolution {\n effectiveName: string;\n selectedExistingScopedName?: string;\n newScopedName?: string;\n nameChanged: boolean;\n}\n\n/**\n * Resolve which name should be used for a save invocation, prompting when needed.\n */\nexport async function resolveEffectiveNameForSave(\n inputName: string,\n profileName?: string,\n prompt?: PromptPort\n): Promise<SaveNameResolution> {\n const prm = prompt ?? resolvePrompt();\n const normalizedInput = normalizePackageName(inputName);\n\n if (isScopedName(normalizedInput)) {\n return {\n effectiveName: normalizedInput,\n nameChanged: false\n };\n }\n\n const scopedVariants = await findScopedVariantsInRegistry(normalizedInput);\n if (scopedVariants.length === 0) {\n return {\n effectiveName: normalizedInput,\n nameChanged: false\n };\n }\n\n const choice = await prm.select<string>(\n `Found scoped packages matching '${normalizedInput}'. How should this save proceed?`,\n [\n ...scopedVariants.map(variant => ({\n title: `Use existing scoped package ${variant}`,\n value: variant,\n description: `Treat this package as '${variant}'`\n })),\n {\n title: 'Create a new scoped name',\n value: '__create_new_scoped__',\n description: 'Create a brand new scoped identity (will prompt for name)'\n },\n {\n title: `Keep unscoped name '${normalizedInput}'`,\n value: '__keep_unscoped__',\n description: 'Continue saving as unscoped (push will still require scoping later)'\n }\n ],\n 'Use arrow keys to select, Enter to confirm'\n );\n\n if (!choice) {\n throw new UserCancellationError('Operation cancelled by user');\n }\n\n if (choice === '__keep_unscoped__') {\n return {\n effectiveName: normalizedInput,\n nameChanged: false\n };\n }\n\n if (choice === '__create_new_scoped__') {\n const newScopedName = await promptForNewScopedName(normalizedInput, profileName, undefined, prm);\n return {\n effectiveName: newScopedName,\n newScopedName,\n nameChanged: newScopedName !== normalizedInput\n };\n }\n\n const normalizedChoice = normalizePackageName(choice);\n return {\n effectiveName: normalizedChoice,\n selectedExistingScopedName: normalizedChoice,\n nameChanged: normalizedChoice !== normalizedInput\n };\n}\n\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;AAkDA,eAAe,mBAAmB,MAAgC;AAChE,MAAM,aAAa,qBAAqB,IAAI;AAC5C,SAAO,MAAM,OAAO,eAAe,UAAU,CAAC;AAChD;AAEA,SAAS,yBAAyB,cAAsB,OAAuB;AAC7E,MAAM,kBAAkB,qBAAqB,MAAM,QAAQ,MAAM,EAAE,CAAC,GAC9D,iBAAiB,qBAAqB,YAAY;AACxD,SAAO,IAAI,eAAe,IAAI,cAAc;AAC9C;AAEA,eAAe,0BAA0B,MAA6B;AACpE,MAAI;AACF,wBAAoB,IAAI;AAAA,EAC1B,SAAS,OAAO;AACd,UAAM,IAAI,MAAO,MAAgB,QAAQ,QAAQ,MAAM,IAAI,CAAC;AAAA,EAC9D;AAEA,MAAI,CAAC,aAAa,IAAI;AACpB,UAAM,IAAI,MAAM,wCAAwC;AAG1D,MAAI,MAAM,mBAAmB,IAAI;AAC/B,UAAM,IAAI;AAAA,MACR,YAAY,IAAI;AAAA,IAClB;AAEJ;AAKA,eAAsB,0BAA0B,aAAmD;AACjG,SAAK,eAIU,MAAM,cAAc,OAAO,GACb,WAAW,WAAW,GAC7B,UAAU,QAL9B;AAMJ;AA0EA,eAAsB,sCACpB,cACA,UACA,aACA,QACiB;AACjB,MAAI,aAAa,YAAY;AAC3B,UAAM,IAAI,MAAM,qCAAqC,YAAY,GAAG;AAGtE,MAAI,CAAC,UAAU,KAAK;AAClB,UAAM,IAAI,MAAM,8CAA8C;AAGhE,MAAM,MAAM,UAAU,cAAc,GAC9B,iBAAiB,qBAAqB,YAAY,GAElD,SAAS,MAAM,IAAI;AAAA,IACvB,YAAY,cAAc;AAAA,IAC1B;AAAA,MACE;AAAA,QACE,OAAO,sBAAsB,QAAQ;AAAA,QACrC,OAAO;AAAA,QACP,aAAa,eAAe,QAAQ,IAAI,cAAc;AAAA,MACxD;AAAA,MACA;AAAA,QACE,OAAO;AAAA,QACP,OAAO;AAAA,QACP,aAAa,4BAA4B,cAAc;AAAA,MACzD;AAAA,IACF;AAAA,IACA;AAAA,EACF;AAEA,MAAI,CAAC;AACH,UAAM,IAAI,sBAAsB,6BAA6B;AAG/D,MAAI,QAAQ;AACZ,MAAI,WAAW,UAAU;AAEvB,QAAM,gBADe,MAAM,0BAA0B,WAAW,IAC7B,QAAQ,MAAM,EAAE,KAAK,UAElD,eAAe,MAAM,IAAI;AAAA,MAC7B,kCAAkC,cAAc;AAAA,MAChD;AAAA,QACE,SAAS;AAAA,QACT,UAAU,OAAO,UAAkB;AACjC,cAAI,CAAC,MAAO,QAAO;AAEnB,cAAM,YAAY,yBAAyB,gBAAgB,KAAK;AAChE,cAAI;AACF,yBAAM,0BAA0B,SAAS,GAClC;AAAA,UACT,SAAS,OAAO;AACd,mBAAQ,MAAgB;AAAA,UAC1B;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,QAAI,CAAC;AACH,YAAM,IAAI,sBAAsB,6BAA6B;AAG/D,YAAQ;AAAA,EACV;AAEA,MAAM,aAAa,yBAAyB,gBAAgB,KAAK;AACjE,eAAM,0BAA0B,UAAU,GACnC;AACT;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
walkFiles
|
|
4
|
+
} from "./chunk-A6ISKBNM.js";
|
|
5
|
+
import {
|
|
6
|
+
createWorkspacePackageYml,
|
|
7
|
+
ensureLocalOpenPackageStructure
|
|
8
|
+
} from "./chunk-CVA64SXK.js";
|
|
9
|
+
import {
|
|
10
|
+
getLocalOpenPackageDir
|
|
11
|
+
} from "./chunk-BCYZDID6.js";
|
|
12
|
+
import {
|
|
13
|
+
parsePackageYml
|
|
14
|
+
} from "./chunk-QTQYI4L5.js";
|
|
15
|
+
import {
|
|
16
|
+
FILE_PATTERNS
|
|
17
|
+
} from "./chunk-J4IFFBLP.js";
|
|
18
|
+
import {
|
|
19
|
+
exists
|
|
20
|
+
} from "./chunk-S47F4OG4.js";
|
|
21
|
+
import {
|
|
22
|
+
logger
|
|
23
|
+
} from "./chunk-5EFWGD33.js";
|
|
24
|
+
|
|
25
|
+
// ../core/src/core/workspace-package-context.ts
|
|
26
|
+
import { join, basename } from "path";
|
|
27
|
+
async function buildWorkspacePackageContext(cwd) {
|
|
28
|
+
await ensureLocalOpenPackageStructure(cwd), await createWorkspacePackageYml(cwd);
|
|
29
|
+
let openpackageDir = getLocalOpenPackageDir(cwd), packageYmlPath = join(openpackageDir, FILE_PATTERNS.OPENPACKAGE_YML), config;
|
|
30
|
+
try {
|
|
31
|
+
config = await parsePackageYml(packageYmlPath);
|
|
32
|
+
} catch (error) {
|
|
33
|
+
throw new Error(
|
|
34
|
+
`Failed to read workspace manifest at ${packageYmlPath}: ${error}`
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
name: config.name || basename(cwd),
|
|
39
|
+
version: config.version,
|
|
40
|
+
config,
|
|
41
|
+
packageYmlPath,
|
|
42
|
+
packageRootDir: openpackageDir,
|
|
43
|
+
packageFilesDir: openpackageDir
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ../core/src/utils/expand-directory-selections.ts
|
|
48
|
+
import { join as join2, relative } from "path";
|
|
49
|
+
import { promises as fs } from "fs";
|
|
50
|
+
import { isJunk } from "junk";
|
|
51
|
+
async function expandDirectorySelections(selectedPaths, basePath) {
|
|
52
|
+
let expandedFiles = [], seenFiles = /* @__PURE__ */ new Set();
|
|
53
|
+
for (let path of selectedPaths)
|
|
54
|
+
if (path.endsWith("/")) {
|
|
55
|
+
let dirPath = path.slice(0, -1), absDirPath = join2(basePath, dirPath);
|
|
56
|
+
try {
|
|
57
|
+
if (!(await fs.stat(absDirPath)).isDirectory())
|
|
58
|
+
continue;
|
|
59
|
+
} catch {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
let filter = (filePath, isDirectory) => {
|
|
63
|
+
if (isDirectory)
|
|
64
|
+
return !0;
|
|
65
|
+
let segments = filePath.split("/"), basename2 = segments[segments.length - 1];
|
|
66
|
+
return !isJunk(basename2);
|
|
67
|
+
};
|
|
68
|
+
for await (let filePath of walkFiles(absDirPath, { filter })) {
|
|
69
|
+
let relativePath = relative(basePath, filePath);
|
|
70
|
+
seenFiles.has(relativePath) || (seenFiles.add(relativePath), expandedFiles.push(relativePath));
|
|
71
|
+
}
|
|
72
|
+
} else
|
|
73
|
+
seenFiles.has(path) || (seenFiles.add(path), expandedFiles.push(path));
|
|
74
|
+
return expandedFiles;
|
|
75
|
+
}
|
|
76
|
+
function hasDirectorySelections(selectedPaths) {
|
|
77
|
+
return selectedPaths.some((path) => path.endsWith("/"));
|
|
78
|
+
}
|
|
79
|
+
function countSelectionTypes(selectedPaths) {
|
|
80
|
+
let dirs = 0, files = 0;
|
|
81
|
+
for (let path of selectedPaths)
|
|
82
|
+
path.endsWith("/") ? dirs++ : files++;
|
|
83
|
+
return { dirs, files };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// src/utils/interactive-file-selector.ts
|
|
87
|
+
import { note, log } from "@clack/prompts";
|
|
88
|
+
|
|
89
|
+
// src/utils/file-selector-with-header.ts
|
|
90
|
+
import { AutocompletePrompt, isCancel } from "@clack/core";
|
|
91
|
+
import { search } from "fast-fuzzy";
|
|
92
|
+
import pico from "picocolors";
|
|
93
|
+
var FileSelectorWithHeader = class extends AutocompletePrompt {
|
|
94
|
+
constructor(options) {
|
|
95
|
+
let fileOptions = options.files.map((file) => ({
|
|
96
|
+
value: file,
|
|
97
|
+
label: file
|
|
98
|
+
}));
|
|
99
|
+
super({
|
|
100
|
+
options: fileOptions,
|
|
101
|
+
multiple: !0,
|
|
102
|
+
render() {
|
|
103
|
+
return this.renderWithHeader();
|
|
104
|
+
}
|
|
105
|
+
}), this.allFiles = options.files, this.message = options.message, this.placeholder = options.placeholder || "Type to search...", this.maxVisibleItems = options.maxItems || 10, this.fuzzyThreshold = options.fuzzyThreshold || 0.5, this.setupFuzzyFiltering(), this.setupNoOverscroll();
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Prevent wrap-around: stay at top when pressing up at first item (don't jump to last)
|
|
109
|
+
*/
|
|
110
|
+
setupNoOverscroll() {
|
|
111
|
+
this.on("key", (_char, key) => {
|
|
112
|
+
let len = this.filteredOptions.length;
|
|
113
|
+
len <= 1 || key.name === "up" && this.cursor === len - 1 && this.emit("key", void 0, { name: "down", ctrl: !1, meta: !1, shift: !1 });
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Setup fuzzy filtering based on user input
|
|
118
|
+
*/
|
|
119
|
+
setupFuzzyFiltering() {
|
|
120
|
+
this.on("userInput", () => {
|
|
121
|
+
this.updateFilteredOptions();
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Update filtered options based on current user input
|
|
126
|
+
*/
|
|
127
|
+
updateFilteredOptions() {
|
|
128
|
+
let input = this.userInput, filteredFiles;
|
|
129
|
+
!input || input.trim() === "" ? filteredFiles = this.allFiles : filteredFiles = search(input, this.allFiles, {
|
|
130
|
+
threshold: this.fuzzyThreshold,
|
|
131
|
+
ignoreCase: !0,
|
|
132
|
+
returnMatchData: !1
|
|
133
|
+
}), this.filteredOptions = filteredFiles.map((file) => ({
|
|
134
|
+
value: file,
|
|
135
|
+
label: file
|
|
136
|
+
}));
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Main render method with dynamic header
|
|
140
|
+
*/
|
|
141
|
+
renderWithHeader() {
|
|
142
|
+
if (this.state === "cancel")
|
|
143
|
+
return this.renderCancelled();
|
|
144
|
+
if (this.state === "submit")
|
|
145
|
+
return this.selectedValues.length === 0 ? this.renderCancelled() : this.renderSubmitted();
|
|
146
|
+
let sections = [];
|
|
147
|
+
return sections.push(this.renderSelectedFilesHeader()), sections.push(this.renderSearchSection()), sections.push(this.renderOptionsList()), sections.push(this.renderFooter()), sections.join(`
|
|
148
|
+
`);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Get a smart label for the current selection (e.g., "2 dirs, 1 file" or "3 files")
|
|
152
|
+
*/
|
|
153
|
+
getSelectionLabel() {
|
|
154
|
+
let count = this.selectedValues.length, dirs = this.selectedValues.filter((v) => v.endsWith("/")).length, files = count - dirs;
|
|
155
|
+
return dirs === 0 ? `${files} file${files === 1 ? "" : "s"}` : files === 0 ? `${dirs} dir${dirs === 1 ? "" : "s"}` : `${dirs} dir${dirs === 1 ? "" : "s"}, ${files} file${files === 1 ? "" : "s"}`;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Render the selected files header using Clack's simple log-style format
|
|
159
|
+
*/
|
|
160
|
+
renderSelectedFilesHeader() {
|
|
161
|
+
let count = this.selectedValues.length, lines = [], title = count === 0 ? `Selected: ${pico.dim("none (use Space to select)")}` : `Selected: ${pico.cyan(this.getSelectionLabel())}`;
|
|
162
|
+
if (lines.push(`${pico.cyan("\u25C6")} ${title}`), count > 0) {
|
|
163
|
+
let displayCount = Math.min(5, count);
|
|
164
|
+
for (let i = 0; i < displayCount; i++) {
|
|
165
|
+
let file = this.selectedValues[i];
|
|
166
|
+
lines.push(`${pico.cyan("\u2502")} ${pico.dim("\u2713 " + file)}`);
|
|
167
|
+
}
|
|
168
|
+
count > displayCount && lines.push(`${pico.cyan("\u2502")} ${pico.dim(`... and ${count - displayCount} more`)}`);
|
|
169
|
+
}
|
|
170
|
+
return lines.push(pico.cyan("\u2502")), lines.join(`
|
|
171
|
+
`);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Render the search section
|
|
175
|
+
*/
|
|
176
|
+
renderSearchSection() {
|
|
177
|
+
let matchCount = this.filteredOptions.length, totalCount = this.allFiles.length, stateSymbol = pico.cyan("\u25C6"), searchLabel = pico.dim("Search:"), input = this.userInput || pico.gray(this.placeholder), cursor = this.state === "active" ? pico.cyan("\u2588") : "", matches = matchCount !== totalCount ? pico.dim(` (${matchCount} ${matchCount === 1 ? "match" : "matches"})`) : "";
|
|
178
|
+
return `${stateSymbol} ${this.message}
|
|
179
|
+
${pico.cyan("\u2502")} ${searchLabel} ${input}${cursor}${matches}`;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Render the options list with sliding window to keep cursor in view
|
|
183
|
+
*/
|
|
184
|
+
renderOptionsList() {
|
|
185
|
+
let lines = [], totalOptions = this.filteredOptions.length, maxItems = this.maxVisibleItems;
|
|
186
|
+
if (totalOptions === 0)
|
|
187
|
+
return lines.push(`${pico.cyan("\u2502")} ${pico.gray("No matches found")}`), lines.join(`
|
|
188
|
+
`);
|
|
189
|
+
let visibleStart = 0;
|
|
190
|
+
this.cursor >= maxItems - 3 && (visibleStart = Math.max(0, Math.min(this.cursor - maxItems + 3, totalOptions - maxItems)));
|
|
191
|
+
let visibleEnd = Math.min(visibleStart + maxItems, totalOptions), visibleOptions = this.filteredOptions.slice(visibleStart, visibleEnd);
|
|
192
|
+
visibleStart > 0 && lines.push(`${pico.cyan("\u2502")} ${pico.gray(`... ${visibleStart} above`)}`);
|
|
193
|
+
for (let i = 0; i < visibleOptions.length; i++) {
|
|
194
|
+
let option = visibleOptions[i], isSelected = this.selectedValues.includes(option.value), isCursor = visibleStart + i === this.cursor, checkbox = isSelected ? pico.cyan("\u25FC") : pico.dim("\u25FB"), cursorMark = isCursor ? pico.cyan("\u25B8") : " ", fileName = option.label;
|
|
195
|
+
isCursor ? fileName = pico.cyan(fileName) : isSelected ? fileName = pico.white(fileName) : fileName = pico.dim(fileName), lines.push(`${pico.cyan("\u2502")} ${cursorMark} ${checkbox} ${fileName}`);
|
|
196
|
+
}
|
|
197
|
+
if (visibleEnd < totalOptions) {
|
|
198
|
+
let remaining = totalOptions - visibleEnd;
|
|
199
|
+
lines.push(`${pico.cyan("\u2502")} ${pico.gray(`... ${remaining} more`)}`);
|
|
200
|
+
}
|
|
201
|
+
return lines.join(`
|
|
202
|
+
`);
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Render the footer with hints
|
|
206
|
+
*/
|
|
207
|
+
renderFooter() {
|
|
208
|
+
let hints = pico.dim("Space: select \u2022 Enter: confirm \u2022 Esc: cancel");
|
|
209
|
+
return `${pico.cyan("\u2514")} ${hints}`;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Render the collapsed cancelled state
|
|
213
|
+
*/
|
|
214
|
+
renderCancelled() {
|
|
215
|
+
let symbol = pico.red("\u25A0"), end = pico.gray("\u2514");
|
|
216
|
+
return `${symbol} ${this.message}
|
|
217
|
+
${end} ${pico.dim("Operation cancelled")}`;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Render the collapsed submitted state (for successful selection)
|
|
221
|
+
*/
|
|
222
|
+
renderSubmitted() {
|
|
223
|
+
let symbol = pico.green("\u25C7"), bar = pico.gray("\u2502"), status = pico.dim(this.getSelectionLabel() + " selected");
|
|
224
|
+
return `${symbol} ${this.message}
|
|
225
|
+
${bar} ${status}`;
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
async function promptFileSelector(options) {
|
|
229
|
+
let result = await new FileSelectorWithHeader(options).prompt();
|
|
230
|
+
return isCancel(result) ? null : result;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// ../core/src/utils/file-scanner.ts
|
|
234
|
+
import { relative as relative2 } from "path";
|
|
235
|
+
import { isJunk as isJunk2 } from "junk";
|
|
236
|
+
var DEFAULT_EXCLUDE_DIRS = /* @__PURE__ */ new Set([
|
|
237
|
+
"node_modules",
|
|
238
|
+
".git",
|
|
239
|
+
".next",
|
|
240
|
+
".turbo",
|
|
241
|
+
"dist",
|
|
242
|
+
"build",
|
|
243
|
+
"out",
|
|
244
|
+
".cache",
|
|
245
|
+
"coverage",
|
|
246
|
+
".nyc_output",
|
|
247
|
+
".parcel-cache",
|
|
248
|
+
".webpack",
|
|
249
|
+
".vscode",
|
|
250
|
+
".idea",
|
|
251
|
+
"__pycache__",
|
|
252
|
+
"target",
|
|
253
|
+
"vendor"
|
|
254
|
+
]);
|
|
255
|
+
function isExcludedDir(fullPath, excludeDirs) {
|
|
256
|
+
let segments = fullPath.split("/");
|
|
257
|
+
for (let segment of segments)
|
|
258
|
+
if (excludeDirs.has(segment))
|
|
259
|
+
return !0;
|
|
260
|
+
return !1;
|
|
261
|
+
}
|
|
262
|
+
async function scanWorkspaceFiles(options = {}) {
|
|
263
|
+
let {
|
|
264
|
+
cwd = process.cwd(),
|
|
265
|
+
basePath,
|
|
266
|
+
excludeDirs = [],
|
|
267
|
+
maxFiles = 1e4,
|
|
268
|
+
followSymlinks = !1,
|
|
269
|
+
includeDirs = !1
|
|
270
|
+
} = options, scanDir = basePath || cwd, allExcludeDirs = /* @__PURE__ */ new Set([
|
|
271
|
+
...DEFAULT_EXCLUDE_DIRS,
|
|
272
|
+
...excludeDirs
|
|
273
|
+
]), files = [], dirs = [], fileCount = 0;
|
|
274
|
+
try {
|
|
275
|
+
let filter = (path, isDirectory) => {
|
|
276
|
+
let segments = path.split("/"), basename2 = segments[segments.length - 1];
|
|
277
|
+
return !(isJunk2(basename2) || isExcludedDir(path, allExcludeDirs));
|
|
278
|
+
};
|
|
279
|
+
for await (let filePath of walkFiles(scanDir, { filter, followSymlinks, includeDirs })) {
|
|
280
|
+
if (fileCount >= maxFiles) {
|
|
281
|
+
logger.debug(`File scan limit reached: ${maxFiles} files`);
|
|
282
|
+
break;
|
|
283
|
+
}
|
|
284
|
+
let relativePath = relative2(scanDir, filePath);
|
|
285
|
+
!relativePath || relativePath === "." || (includeDirs && (await (await import("fs")).promises.stat(filePath)).isDirectory() ? dirs.push(relativePath + "/") : files.push(relativePath), fileCount++);
|
|
286
|
+
}
|
|
287
|
+
let allItems = [...dirs.sort(), ...files.sort()];
|
|
288
|
+
return allItems.sort((a, b) => {
|
|
289
|
+
let aPath = a.endsWith("/") ? a.slice(0, -1) : a, bPath = b.endsWith("/") ? b.slice(0, -1) : b, aDepth = aPath.split("/").length, bDepth = bPath.split("/").length;
|
|
290
|
+
return aDepth !== bDepth ? aDepth - bDepth : a.localeCompare(b);
|
|
291
|
+
}), logger.debug(`Scanned ${allItems.length} items in workspace`, { scanDir, includeDirs }), allItems;
|
|
292
|
+
} catch (error) {
|
|
293
|
+
throw logger.error("Error scanning workspace files", { error, scanDir }), new Error(`Failed to scan workspace files: ${error}`);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// src/utils/interactive-file-selector.ts
|
|
298
|
+
async function interactiveFileSelect(options = {}) {
|
|
299
|
+
let {
|
|
300
|
+
cwd = process.cwd(),
|
|
301
|
+
basePath,
|
|
302
|
+
message,
|
|
303
|
+
placeholder,
|
|
304
|
+
maxItems = 10,
|
|
305
|
+
excludeDirs = [],
|
|
306
|
+
fuzzyThreshold = 0.5,
|
|
307
|
+
includeDirs = !1
|
|
308
|
+
} = options, finalMessage = message || (includeDirs ? "Select files or directories to add" : "Select files to add"), finalPlaceholder = placeholder || (includeDirs ? "Type to search..." : "Type to search files..."), scanDir = basePath || cwd;
|
|
309
|
+
try {
|
|
310
|
+
logger.debug("Scanning workspace for files", { scanDir });
|
|
311
|
+
let allFiles = await scanWorkspaceFiles({ cwd, basePath, excludeDirs, includeDirs });
|
|
312
|
+
if (allFiles.length === 0)
|
|
313
|
+
return logger.warn("No files found in workspace"), note("No files found in the workspace directory", "No Files"), null;
|
|
314
|
+
logger.debug(`Found ${allFiles.length} files to display`);
|
|
315
|
+
let selectedFiles = await promptFileSelector({
|
|
316
|
+
message: finalMessage,
|
|
317
|
+
files: allFiles,
|
|
318
|
+
placeholder: finalPlaceholder,
|
|
319
|
+
maxItems,
|
|
320
|
+
fuzzyThreshold
|
|
321
|
+
});
|
|
322
|
+
if (!selectedFiles || selectedFiles.length === 0)
|
|
323
|
+
return logger.debug("No files selected or user cancelled"), null;
|
|
324
|
+
logger.debug(`User selected ${selectedFiles.length} file(s)`, { selectedFiles });
|
|
325
|
+
let fileList = selectedFiles.length <= 5 ? selectedFiles.join(`
|
|
326
|
+
`) : selectedFiles.slice(0, 5).join(`
|
|
327
|
+
`) + `
|
|
328
|
+
... and ${selectedFiles.length - 5} more`, countLabel = includeDirs ? (() => {
|
|
329
|
+
let { dirs, files } = countSelectionTypes(selectedFiles), parts = [];
|
|
330
|
+
return dirs > 0 && parts.push(`${dirs} dir${dirs === 1 ? "" : "s"}`), files > 0 && parts.push(`${files} file${files === 1 ? "" : "s"}`), parts.join(" and ");
|
|
331
|
+
})() : `${selectedFiles.length} file${selectedFiles.length === 1 ? "" : "s"}`;
|
|
332
|
+
return log.success(`Selected ${countLabel}:
|
|
333
|
+
${fileList}`), selectedFiles;
|
|
334
|
+
} catch (error) {
|
|
335
|
+
throw logger.error("Error during file selection", { error }), new Error(`File selection failed: ${error}`);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// ../core/src/utils/source-operation-arguments.ts
|
|
340
|
+
import { resolve as resolvePath, join as join3 } from "path";
|
|
341
|
+
async function resolveSourceOperationArguments(cwd, packageName, pathArg, options) {
|
|
342
|
+
let { command, checkWorkspaceRoot = !1 } = options, flagName = command === "add" ? "--to" : "--from";
|
|
343
|
+
if (packageName && pathArg)
|
|
344
|
+
return { resolvedPackageName: packageName, resolvedPath: pathArg };
|
|
345
|
+
let singleArg = packageName || pathArg;
|
|
346
|
+
if (!singleArg)
|
|
347
|
+
throw new Error(`Path argument is required for ${command}.`);
|
|
348
|
+
let absPath = resolvePath(cwd, singleArg), pathExists = await exists(absPath);
|
|
349
|
+
if (!pathExists && checkWorkspaceRoot) {
|
|
350
|
+
let openpackageDir = getLocalOpenPackageDir(cwd), workspaceRootPath = join3(openpackageDir, singleArg);
|
|
351
|
+
pathExists = await exists(workspaceRootPath);
|
|
352
|
+
}
|
|
353
|
+
if (pathExists)
|
|
354
|
+
return { resolvedPackageName: null, resolvedPath: singleArg };
|
|
355
|
+
if (checkWorkspaceRoot && command === "remove")
|
|
356
|
+
return { resolvedPackageName: null, resolvedPath: singleArg };
|
|
357
|
+
throw new Error(
|
|
358
|
+
`Path '${singleArg}' not found.
|
|
359
|
+
|
|
360
|
+
If you meant to specify a package name, use: opkg ${command} <path> ${flagName} ${singleArg}`
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
export {
|
|
365
|
+
resolveSourceOperationArguments,
|
|
366
|
+
buildWorkspacePackageContext,
|
|
367
|
+
expandDirectorySelections,
|
|
368
|
+
hasDirectorySelections,
|
|
369
|
+
interactiveFileSelect
|
|
370
|
+
};
|
|
371
|
+
//# sourceMappingURL=chunk-PSQXKAL4.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../core/src/core/workspace-package-context.ts", "../../core/src/utils/expand-directory-selections.ts", "../src/utils/interactive-file-selector.ts", "../src/utils/file-selector-with-header.ts", "../../core/src/utils/file-scanner.ts", "../../core/src/utils/source-operation-arguments.ts"],
|
|
4
|
+
"sourcesContent": ["import { join, basename } from 'path';\n\nimport { FILE_PATTERNS } from '../constants/index.js';\nimport { ensureLocalOpenPackageStructure, createWorkspacePackageYml } from './package-management.js';\nimport { getLocalOpenPackageDir } from '../utils/paths.js';\nimport { parsePackageYml } from '../utils/package-yml.js';\nimport type { PackageYml } from '../types/index.js';\n\n/**\n * Workspace package context for add/remove operations.\n * Contains essential package metadata for operating on workspace root (.openpackage/).\n */\nexport interface WorkspacePackageContext {\n /** Package name (from manifest or workspace directory name) */\n name: string;\n /** Package version (if specified in manifest) */\n version?: string;\n /** Full package configuration from openpackage.yml */\n config: PackageYml;\n /** Absolute path to package manifest (openpackage.yml) */\n packageYmlPath: string;\n /** Root directory of the package (.openpackage/) */\n packageRootDir: string;\n /** Directory containing package files (same as root for workspace packages) */\n packageFilesDir: string;\n}\n\n/**\n * Build workspace package context for operations on workspace root (.openpackage/).\n * \n * This utility creates or ensures the workspace package structure exists and loads\n * the manifest configuration. Used by add/remove commands when operating on the\n * workspace root (no specific package specified via --to/--from).\n * \n * @param cwd - Current working directory\n * @returns Workspace package context with loaded configuration\n * @throws Error if manifest cannot be read\n * \n * @example\n * // Add command: opkg add ./file.md (no --to option)\n * const context = await buildWorkspacePackageContext(cwd);\n * // Files will be added to context.packageRootDir (.openpackage/)\n * \n * @example\n * // Remove command: opkg remove agents/my-agent (no --from option)\n * const context = await buildWorkspacePackageContext(cwd);\n * // Files will be removed from context.packageRootDir (.openpackage/)\n */\nexport async function buildWorkspacePackageContext(\n cwd: string\n): Promise<WorkspacePackageContext> {\n // Ensure .openpackage/ structure exists\n await ensureLocalOpenPackageStructure(cwd);\n\n // Create workspace manifest if it doesn't exist\n await createWorkspacePackageYml(cwd);\n\n const openpackageDir = getLocalOpenPackageDir(cwd);\n const packageYmlPath = join(openpackageDir, FILE_PATTERNS.OPENPACKAGE_YML);\n\n // Load workspace manifest\n let config: PackageYml;\n try {\n config = await parsePackageYml(packageYmlPath);\n } catch (error) {\n throw new Error(\n `Failed to read workspace manifest at ${packageYmlPath}: ${error}`\n );\n }\n\n // Use workspace directory name as package name if not specified in manifest\n const packageName = config.name || basename(cwd);\n\n return {\n name: packageName,\n version: config.version,\n config,\n packageYmlPath,\n packageRootDir: openpackageDir,\n packageFilesDir: openpackageDir\n };\n}\n\n/**\n * Ensure workspace package structure exists and is initialized.\n * This is a convenience wrapper around ensureLocalOpenPackageStructure and createWorkspacePackageYml.\n * \n * @param cwd - Current working directory\n * \n * @example\n * // Ensure workspace is ready before performing operations\n * await ensureWorkspacePackageExists(cwd);\n * // Now .openpackage/ directory and openpackage.yml exist\n */\nexport async function ensureWorkspacePackageExists(cwd: string): Promise<void> {\n await ensureLocalOpenPackageStructure(cwd);\n await createWorkspacePackageYml(cwd);\n}\n", "/**\n * Directory Selection Expansion Utility\n * \n * Expands directory selections (paths ending with '/') into all files within them.\n * Used by add and remove commands to handle directory selections from interactive file selector.\n */\n\nimport { join, relative } from 'path';\nimport { promises as fs } from 'fs';\nimport { walkFiles } from './file-walker.js';\nimport { isJunk } from 'junk';\n\n/**\n * Expand directory selections into individual file paths\n * \n * @param selectedPaths - Array of selected paths (files and directories with '/' suffix)\n * @param basePath - Base directory path for resolving relative paths\n * @returns Array of individual file paths (no directories)\n * \n * @example\n * const selections = ['src/', 'package.json', 'docs/readme.md'];\n * const expanded = await expandDirectorySelections(selections, '/workspace');\n * // Returns: ['src/index.ts', 'src/utils/helper.ts', 'package.json', 'docs/readme.md']\n */\nexport async function expandDirectorySelections(\n selectedPaths: string[],\n basePath: string\n): Promise<string[]> {\n const expandedFiles: string[] = [];\n const seenFiles = new Set<string>();\n \n for (const path of selectedPaths) {\n if (path.endsWith('/')) {\n // This is a directory - expand it to all files within\n const dirPath = path.slice(0, -1); // Remove trailing '/'\n const absDirPath = join(basePath, dirPath);\n \n // Check if directory exists\n try {\n const stat = await fs.stat(absDirPath);\n if (!stat.isDirectory()) {\n // Not a directory, skip\n continue;\n }\n } catch {\n // Directory doesn't exist or is inaccessible, skip\n continue;\n }\n \n // Walk all files in the directory\n const filter = (filePath: string, isDirectory: boolean) => {\n if (isDirectory) {\n return true; // Always traverse directories\n }\n \n // Filter out junk files\n const segments = filePath.split('/');\n const basename = segments[segments.length - 1];\n return !isJunk(basename);\n };\n \n for await (const filePath of walkFiles(absDirPath, { filter })) {\n // Convert to relative path from basePath (path.relative handles trailing slashes correctly)\n const relativePath = relative(basePath, filePath);\n \n // Only add if not already seen (avoid duplicates)\n if (!seenFiles.has(relativePath)) {\n seenFiles.add(relativePath);\n expandedFiles.push(relativePath);\n }\n }\n } else {\n // This is a regular file - add it directly if not already seen\n if (!seenFiles.has(path)) {\n seenFiles.add(path);\n expandedFiles.push(path);\n }\n }\n }\n \n return expandedFiles;\n}\n\n/**\n * Check if any selected paths are directories\n * \n * @param selectedPaths - Array of selected paths\n * @returns True if at least one path is a directory (ends with '/')\n */\nexport function hasDirectorySelections(selectedPaths: string[]): boolean {\n return selectedPaths.some(path => path.endsWith('/'));\n}\n\n/**\n * Count how many directories and files are in the selection\n * \n * @param selectedPaths - Array of selected paths\n * @returns Object with counts of directories and files\n */\nexport function countSelectionTypes(selectedPaths: string[]): { dirs: number; files: number } {\n let dirs = 0;\n let files = 0;\n \n for (const path of selectedPaths) {\n if (path.endsWith('/')) {\n dirs++;\n } else {\n files++;\n }\n }\n \n return { dirs, files };\n}\n", "/**\n * Interactive File Selector\n * \n * Provides fuzzy file selection with dynamic header showing selected files.\n * Uses custom AutocompletePrompt that updates the header in real-time.\n */\n\nimport { note, log } from '@clack/prompts';\nimport { promptFileSelector } from './file-selector-with-header.js';\nimport { scanWorkspaceFiles } from '@opkg/core/utils/file-scanner.js';\nimport { countSelectionTypes } from '@opkg/core/utils/expand-directory-selections.js';\nimport { logger } from '@opkg/core/utils/logger.js';\n\n/**\n * Options for interactive file selection\n */\nexport interface FileSelectionOptions {\n /** Base directory to scan from (default: process.cwd()) */\n cwd?: string;\n \n /** Specific directory path to scan (overrides cwd if provided) */\n basePath?: string;\n \n /** Prompt message to display (default: 'Select files') */\n message?: string;\n \n /** Placeholder text when no input (default: 'Type to search files...') */\n placeholder?: string;\n \n /** Maximum items to display at once (default: 10) */\n maxItems?: number;\n \n /** Additional directories to exclude from scanning */\n excludeDirs?: string[];\n \n /** Fuzzy search threshold (0-1, default: 0.5) */\n fuzzyThreshold?: number;\n \n /** Whether to include directories in results (default: false) */\n includeDirs?: boolean;\n}\n\n/**\n * Display an interactive fuzzy file selector with dynamic header\n * \n * Features:\n * - Dynamic header showing selected files (updates in real-time)\n * - Fuzzy search with real-time filtering\n * - Space to toggle selection\n * - Enter to confirm\n * \n * @param options - Selection options\n * @returns Array of selected relative file paths, or null if cancelled\n * \n * @example\n * const files = await interactiveFileSelect({ cwd: process.cwd() });\n * if (files) {\n * console.log('Selected:', files);\n * } else {\n * console.log('Cancelled');\n * }\n */\nexport async function interactiveFileSelect(\n options: FileSelectionOptions = {}\n): Promise<string[] | null> {\n const {\n cwd = process.cwd(),\n basePath,\n message,\n placeholder,\n maxItems = 10,\n excludeDirs = [],\n fuzzyThreshold = 0.5,\n includeDirs = false\n } = options;\n \n // Set smart defaults based on whether directories are included\n const finalMessage = message || (includeDirs ? 'Select files or directories to add' : 'Select files to add');\n const finalPlaceholder = placeholder || (includeDirs ? 'Type to search...' : 'Type to search files...');\n \n const scanDir = basePath || cwd;\n \n try {\n // Scan workspace for all files\n logger.debug('Scanning workspace for files', { scanDir });\n const allFiles = await scanWorkspaceFiles({ cwd, basePath, excludeDirs, includeDirs });\n \n // Check if any files found\n if (allFiles.length === 0) {\n logger.warn('No files found in workspace');\n note('No files found in the workspace directory', 'No Files');\n return null;\n }\n \n logger.debug(`Found ${allFiles.length} files to display`);\n \n // Display the file selector with dynamic header\n const selectedFiles = await promptFileSelector({\n message: finalMessage,\n files: allFiles,\n placeholder: finalPlaceholder,\n maxItems,\n fuzzyThreshold\n });\n \n // Check if user cancelled or selected nothing\n if (!selectedFiles || selectedFiles.length === 0) {\n logger.debug('No files selected or user cancelled');\n return null; // Prompt handles its own cancellation display\n }\n \n logger.debug(`User selected ${selectedFiles.length} file(s)`, { selectedFiles });\n\n // Show summary after selection (use dir/file counts when includeDirs)\n const fileList = selectedFiles.length <= 5\n ? selectedFiles.join('\\n')\n : selectedFiles.slice(0, 5).join('\\n') + `\\n... and ${selectedFiles.length - 5} more`;\n const countLabel = includeDirs\n ? (() => {\n const { dirs, files } = countSelectionTypes(selectedFiles);\n const parts: string[] = [];\n if (dirs > 0) parts.push(`${dirs} dir${dirs === 1 ? '' : 's'}`);\n if (files > 0) parts.push(`${files} file${files === 1 ? '' : 's'}`);\n return parts.join(' and ');\n })()\n : `${selectedFiles.length} file${selectedFiles.length === 1 ? '' : 's'}`;\n log.success(`Selected ${countLabel}:\\n${fileList}`);\n \n return selectedFiles;\n \n } catch (error) {\n logger.error('Error during file selection', { error });\n throw new Error(`File selection failed: ${error}`);\n }\n}\n\n/**\n * Display an interactive fuzzy file selector for single file selection\n * \n * @param options - Selection options\n * @returns Selected relative file path, or null if cancelled\n */\nexport async function interactiveFileSelectSingle(\n options: FileSelectionOptions = {}\n): Promise<string | null> {\n const result = await interactiveFileSelect({\n ...options,\n message: options.message || 'Select a file'\n });\n \n if (!result || result.length === 0) {\n return null;\n }\n \n // Return the first selected file\n return result[0];\n}\n", "/**\n * File Selector with Dynamic Header\n * \n * Custom AutocompletePrompt that shows selected files in a dynamic header\n * that updates in real-time as selections change.\n */\n\nimport { AutocompletePrompt, isCancel } from '@clack/core';\nimport { search } from 'fast-fuzzy';\nimport pico from 'picocolors';\nimport type { Key } from 'node:readline';\n\n/**\n * Options for file selector with dynamic header\n */\nexport interface FileSelectorOptions {\n /** Prompt message */\n message: string;\n \n /** All available files */\n files: string[];\n \n /** Placeholder text */\n placeholder?: string;\n \n /** Maximum items to show */\n maxItems?: number;\n \n /** Fuzzy search threshold */\n fuzzyThreshold?: number;\n}\n\n/**\n * File option for autocomplete\n */\ninterface FileOption {\n value: string;\n label: string;\n}\n\n/**\n * Custom AutocompletePrompt that shows selected files above the search UI\n */\nexport class FileSelectorWithHeader extends AutocompletePrompt<FileOption> {\n private allFiles: string[];\n private fuzzyThreshold: number;\n private message: string;\n private placeholder: string;\n private maxVisibleItems: number;\n\n constructor(options: FileSelectorOptions) {\n const fileOptions: FileOption[] = options.files.map(file => ({\n value: file,\n label: file\n }));\n\n super({\n options: fileOptions,\n multiple: true,\n render() {\n return this.renderWithHeader();\n }\n } as any);\n\n this.allFiles = options.files;\n this.message = options.message;\n this.placeholder = options.placeholder || 'Type to search...';\n this.maxVisibleItems = options.maxItems || 10;\n this.fuzzyThreshold = options.fuzzyThreshold || 0.5;\n\n // Override the options to provide dynamic filtering\n this.setupFuzzyFiltering();\n\n // Disable wrap-around: stay at top when pressing up at first item\n this.setupNoOverscroll();\n }\n\n /**\n * Prevent wrap-around: stay at top when pressing up at first item (don't jump to last)\n */\n private setupNoOverscroll(): void {\n this.on('key', (_char: string | undefined, key: Key) => {\n const len = this.filteredOptions.length;\n if (len <= 1) return;\n\n if (key.name === 'up' && this.cursor === len - 1) {\n // Cursor wrapped from 0 to end; emit synthetic down to undo\n this.emit('key', undefined, { name: 'down', ctrl: false, meta: false, shift: false } as Key);\n }\n });\n }\n\n /**\n * Setup fuzzy filtering based on user input\n */\n private setupFuzzyFiltering(): void {\n // Listen to userInput changes to update filtered options\n this.on('userInput', () => {\n this.updateFilteredOptions();\n });\n }\n\n /**\n * Update filtered options based on current user input\n */\n private updateFilteredOptions(): void {\n const input = this.userInput;\n \n let filteredFiles: string[];\n \n if (!input || input.trim() === '') {\n filteredFiles = this.allFiles;\n } else {\n filteredFiles = search(input, this.allFiles, {\n threshold: this.fuzzyThreshold,\n ignoreCase: true,\n returnMatchData: false\n }) as string[];\n }\n\n // Update filteredOptions (this is a public property we can modify)\n this.filteredOptions = filteredFiles.map(file => ({\n value: file,\n label: file\n }));\n }\n\n /**\n * Main render method with dynamic header\n */\n private renderWithHeader(): string {\n // Handle final states with collapsed rendering\n if (this.state === 'cancel') {\n return this.renderCancelled();\n }\n \n if (this.state === 'submit') {\n // Treat empty selection as cancellation\n if (this.selectedValues.length === 0) {\n return this.renderCancelled();\n }\n return this.renderSubmitted();\n }\n \n // Render full interactive UI for active/initial/error states\n const sections: string[] = [];\n\n // === DYNAMIC HEADER SECTION ===\n sections.push(this.renderSelectedFilesHeader());\n\n // === PROMPT SECTION ===\n sections.push(this.renderSearchSection());\n\n // === OPTIONS LIST ===\n sections.push(this.renderOptionsList());\n\n // === FOOTER ===\n sections.push(this.renderFooter());\n\n return sections.join('\\n');\n }\n\n /**\n * Get a smart label for the current selection (e.g., \"2 dirs, 1 file\" or \"3 files\")\n */\n private getSelectionLabel(): string {\n const count = this.selectedValues.length;\n const dirs = this.selectedValues.filter(v => v.endsWith('/')).length;\n const files = count - dirs;\n \n if (dirs === 0) {\n return `${files} file${files === 1 ? '' : 's'}`;\n } else if (files === 0) {\n return `${dirs} dir${dirs === 1 ? '' : 's'}`;\n } else {\n return `${dirs} dir${dirs === 1 ? '' : 's'}, ${files} file${files === 1 ? '' : 's'}`;\n }\n }\n\n /**\n * Render the selected files header using Clack's simple log-style format\n */\n private renderSelectedFilesHeader(): string {\n const count = this.selectedValues.length;\n const lines: string[] = [];\n \n // Title line with pointer symbol (\u25C6) for consistency with prompt message\n const title = count === 0 \n ? `Selected: ${pico.dim('none (use Space to select)')}`\n : `Selected: ${pico.cyan(this.getSelectionLabel())}`;\n \n lines.push(`${pico.cyan('\u25C6')} ${title}`);\n \n // Show selected files (up to 5)\n if (count > 0) {\n const displayCount = Math.min(5, count);\n for (let i = 0; i < displayCount; i++) {\n const file = this.selectedValues[i];\n lines.push(`${pico.cyan('\u2502')} ${pico.dim('\u2713 ' + file)}`);\n }\n \n // Show \"... and X more\" if there are more\n if (count > displayCount) {\n lines.push(`${pico.cyan('\u2502')} ${pico.dim(`... and ${count - displayCount} more`)}`);\n }\n }\n \n // Separator line\n lines.push(pico.cyan('\u2502'));\n \n return lines.join('\\n');\n }\n\n /**\n * Render the search section\n */\n private renderSearchSection(): string {\n const matchCount = this.filteredOptions.length;\n const totalCount = this.allFiles.length;\n\n const stateSymbol = pico.cyan('\u25C6');\n const searchLabel = pico.dim('Search:');\n const input = this.userInput || pico.gray(this.placeholder);\n const cursor = this.state === 'active' ? pico.cyan('\u2588') : '';\n const matches = matchCount !== totalCount\n ? pico.dim(` (${matchCount} ${matchCount === 1 ? 'match' : 'matches'})`)\n : '';\n\n return `${stateSymbol} ${this.message}\\n${pico.cyan('\u2502')} ${searchLabel} ${input}${cursor}${matches}`;\n }\n\n /**\n * Render the options list with sliding window to keep cursor in view\n */\n private renderOptionsList(): string {\n const lines: string[] = [];\n const totalOptions = this.filteredOptions.length;\n const maxItems = this.maxVisibleItems;\n\n if (totalOptions === 0) {\n lines.push(`${pico.cyan('\u2502')} ${pico.gray('No matches found')}`);\n return lines.join('\\n');\n }\n\n // Sliding window: scroll so cursor stays in view\n let visibleStart = 0;\n if (this.cursor >= maxItems - 3) {\n visibleStart = Math.max(0, Math.min(this.cursor - maxItems + 3, totalOptions - maxItems));\n }\n const visibleEnd = Math.min(visibleStart + maxItems, totalOptions);\n const visibleOptions = this.filteredOptions.slice(visibleStart, visibleEnd);\n\n // Show \"more above\" indicator when scrolled down\n if (visibleStart > 0) {\n lines.push(`${pico.cyan('\u2502')} ${pico.gray(`... ${visibleStart} above`)}`);\n }\n\n for (let i = 0; i < visibleOptions.length; i++) {\n const option = visibleOptions[i];\n const isSelected = this.selectedValues.includes(option.value);\n const isCursor = visibleStart + i === this.cursor;\n\n // Checkbox\n const checkbox = isSelected ? pico.cyan('\u25FC') : pico.dim('\u25FB');\n\n // Cursor indicator\n const cursorMark = isCursor ? pico.cyan('\u25B8') : ' ';\n\n // File name\n let fileName = option.label;\n if (isCursor) {\n fileName = pico.cyan(fileName);\n } else if (isSelected) {\n fileName = pico.white(fileName);\n } else {\n fileName = pico.dim(fileName);\n }\n\n lines.push(`${pico.cyan('\u2502')} ${cursorMark} ${checkbox} ${fileName}`);\n }\n\n // Show \"more below\" indicator when there are more items\n if (visibleEnd < totalOptions) {\n const remaining = totalOptions - visibleEnd;\n lines.push(`${pico.cyan('\u2502')} ${pico.gray(`... ${remaining} more`)}`);\n }\n\n return lines.join('\\n');\n }\n\n /**\n * Render the footer with hints\n */\n private renderFooter(): string {\n const hints = pico.dim('Space: select \u2022 Enter: confirm \u2022 Esc: cancel');\n return `${pico.cyan('\u2514')} ${hints}`;\n }\n\n /**\n * Render the collapsed cancelled state\n */\n private renderCancelled(): string {\n const symbol = pico.red('\u25A0'); // Red square for cancelled\n const end = pico.gray('\u2514');\n \n return `${symbol} ${this.message}\\n${end} ${pico.dim('Operation cancelled')}`;\n }\n\n /**\n * Render the collapsed submitted state (for successful selection)\n */\n private renderSubmitted(): string {\n const symbol = pico.green('\u25C7'); // Green hollow diamond for success\n const bar = pico.gray('\u2502');\n \n const status = pico.dim(this.getSelectionLabel() + ' selected');\n \n return `${symbol} ${this.message}\\n${bar} ${status}`;\n }\n}\n\n/**\n * Helper function to create and run the file selector\n */\nexport async function promptFileSelector(\n options: FileSelectorOptions\n): Promise<string[] | null> {\n const selector = new FileSelectorWithHeader(options);\n const result = await selector.prompt();\n\n if (isCancel(result)) {\n return null;\n }\n\n return result as string[];\n}\n", "/**\n * File Scanner Utility\n * \n * Scans all files in a workspace directory for interactive file selection.\n * Filters out junk files and common build/dependency directories.\n */\n\nimport { relative } from 'path';\nimport { isJunk } from 'junk';\nimport { walkFiles } from './file-walker.js';\nimport { logger } from './logger.js';\n\n/**\n * Common directories to exclude from file scanning\n */\nconst DEFAULT_EXCLUDE_DIRS = new Set([\n 'node_modules',\n '.git',\n '.next',\n '.turbo',\n 'dist',\n 'build',\n 'out',\n '.cache',\n 'coverage',\n '.nyc_output',\n '.parcel-cache',\n '.webpack',\n '.vscode',\n '.idea',\n '__pycache__',\n 'target',\n 'vendor',\n]);\n\n/**\n * Options for scanning workspace files\n */\nexport interface FileScanOptions {\n /** Base directory to scan from (default: process.cwd()) */\n cwd?: string;\n \n /** Specific directory path to scan (overrides cwd if provided) */\n basePath?: string;\n \n /** Additional directory names to exclude (merged with defaults) */\n excludeDirs?: string[];\n \n /** Maximum number of files to return (default: 10000) */\n maxFiles?: number;\n \n /** Whether to follow symbolic links (default: false) */\n followSymlinks?: boolean;\n \n /** Whether to include directories in results (default: false) */\n includeDirs?: boolean;\n}\n\n/**\n * Check if a path segment is an excluded directory\n */\nfunction isExcludedDir(fullPath: string, excludeDirs: Set<string>): boolean {\n const segments = fullPath.split('/');\n \n // Check if any segment matches an excluded directory\n for (const segment of segments) {\n if (excludeDirs.has(segment)) {\n return true;\n }\n }\n \n return false;\n}\n\n/**\n * Scan workspace for all non-junk files\n * \n * @param options - Scanning options\n * @returns Array of relative file paths (and optionally directory paths with '/' suffix) from cwd (or basePath if specified)\n * \n * @example\n * const files = await scanWorkspaceFiles({ cwd: '/path/to/workspace' });\n * // Returns: ['src/index.ts', 'package.json', '.openpkgs/rules/cursor.md', ...]\n * \n * @example\n * // Scan a specific directory\n * const packageFiles = await scanWorkspaceFiles({ \n * cwd: '/workspace',\n * basePath: '/workspace/.openpackage/packages/my-pkg'\n * });\n * // Returns files relative to basePath: ['file1.txt', 'subdir/file2.txt', ...]\n * \n * @example\n * // Include directories in results\n * const filesAndDirs = await scanWorkspaceFiles({ \n * cwd: '/path/to/workspace',\n * includeDirs: true\n * });\n * // Returns: ['src/', 'src/index.ts', 'src/utils/', 'src/utils/helper.ts', ...]\n */\nexport async function scanWorkspaceFiles(\n options: FileScanOptions = {}\n): Promise<string[]> {\n const {\n cwd = process.cwd(),\n basePath,\n excludeDirs = [],\n maxFiles = 10000,\n followSymlinks = false,\n includeDirs = false\n } = options;\n \n // Use basePath if provided, otherwise use cwd\n const scanDir = basePath || cwd;\n \n // Merge default and custom exclude directories\n const allExcludeDirs = new Set([\n ...DEFAULT_EXCLUDE_DIRS,\n ...excludeDirs\n ]);\n \n const files: string[] = [];\n const dirs: string[] = [];\n let fileCount = 0;\n \n try {\n // Create filter for file walker\n const filter = (path: string, isDirectory: boolean) => {\n // Get the basename for junk checking\n const segments = path.split('/');\n const basename = segments[segments.length - 1];\n \n // Filter out junk files\n if (isJunk(basename)) {\n return false;\n }\n \n // Filter out excluded directories and their contents\n if (isExcludedDir(path, allExcludeDirs)) {\n return false;\n }\n \n return true;\n };\n \n // Walk files and collect relative paths\n for await (const filePath of walkFiles(scanDir, { filter, followSymlinks, includeDirs })) {\n // Stop if we've hit the max files limit\n if (fileCount >= maxFiles) {\n logger.debug(`File scan limit reached: ${maxFiles} files`);\n break;\n }\n \n // Convert to relative path for display\n const relativePath = relative(scanDir, filePath);\n \n // Skip if relative path is empty (shouldn't happen, but safety check)\n if (!relativePath || relativePath === '.') {\n continue;\n }\n \n // Check if this is a directory (when includeDirs is enabled)\n if (includeDirs) {\n const fs = await import('fs');\n const stat = await fs.promises.stat(filePath);\n if (stat.isDirectory()) {\n // Add '/' suffix to directories for visual distinction\n dirs.push(relativePath + '/');\n } else {\n files.push(relativePath);\n }\n } else {\n files.push(relativePath);\n }\n \n fileCount++;\n }\n \n // Sort files and directories for better UX\n // Directories first, then files, both alphabetically within their group\n const allItems = [...dirs.sort(), ...files.sort()];\n \n // Apply depth-based sorting to prioritize shallow items\n allItems.sort((a, b) => {\n // Remove trailing '/' for directory depth calculation\n const aPath = a.endsWith('/') ? a.slice(0, -1) : a;\n const bPath = b.endsWith('/') ? b.slice(0, -1) : b;\n \n // Prioritize items in root directory\n const aDepth = aPath.split('/').length;\n const bDepth = bPath.split('/').length;\n \n if (aDepth !== bDepth) {\n return aDepth - bDepth;\n }\n \n // Then alphabetically\n return a.localeCompare(b);\n });\n \n logger.debug(`Scanned ${allItems.length} items in workspace`, { scanDir, includeDirs });\n \n return allItems;\n \n } catch (error) {\n logger.error('Error scanning workspace files', { error, scanDir });\n throw new Error(`Failed to scan workspace files: ${error}`);\n }\n}\n\n/**\n * Check if the current environment supports interactive prompts.\n *\n * Prefer using `ExecutionContext.interactive` when available -- this\n * function exists as a last-resort fallback for code paths that\n * don't have access to a context object.\n * \n * @returns True if stdin and stdout are TTY (terminal)\n * @deprecated Use ExecutionContext.interactive instead when possible.\n */\nexport function canPrompt(): boolean {\n return Boolean(process.stdin.isTTY && process.stdout.isTTY);\n}\n", "import { resolve as resolvePath, join } from 'path';\n\nimport { exists } from './fs.js';\nimport { getLocalOpenPackageDir } from './paths.js';\n\n/**\n * Options for resolving source operation arguments.\n */\nexport interface SourceOperationOptions {\n /** Command name for error messages ('add' | 'remove') */\n command: 'add' | 'remove';\n /** Whether to check workspace root path in addition to filesystem path (for remove) */\n checkWorkspaceRoot?: boolean;\n}\n\n/**\n * Resolved source operation result.\n */\nexport interface ResolvedSourceOperation {\n /** Package name (null = workspace root, string = specific package) */\n resolvedPackageName: string | null;\n /** Path to operate on */\n resolvedPath: string;\n}\n\n/**\n * Resolve source operation arguments to determine package name and operation path.\n * \n * This shared utility handles argument resolution for both add and remove commands:\n * - For add: packageName from --to option, pathArg is source path to add\n * - For remove: packageName from --from option, pathArg is target path to remove\n * \n * @param cwd - Current working directory\n * @param packageName - Package name from --to/--from option (undefined = workspace root)\n * @param pathArg - Path argument from command line\n * @param options - Command-specific options\n * @returns Resolved package name and path\n * \n * @example\n * // Add command: opkg add ./file.md --to my-package\n * const result = await resolveSourceOperationArguments(\n * cwd, 'my-package', './file.md', { command: 'add' }\n * );\n * // => { resolvedPackageName: 'my-package', resolvedPath: './file.md' }\n * \n * @example\n * // Remove command: opkg remove agents/my-agent\n * const result = await resolveSourceOperationArguments(\n * cwd, undefined, 'agents/my-agent', { command: 'remove', checkWorkspaceRoot: true }\n * );\n * // Checks both ./agents/my-agent AND .openpackage/agents/my-agent\n * // => { resolvedPackageName: null, resolvedPath: 'agents/my-agent' }\n */\nexport async function resolveSourceOperationArguments(\n cwd: string,\n packageName: string | undefined,\n pathArg: string | undefined,\n options: SourceOperationOptions\n): Promise<ResolvedSourceOperation> {\n const { command, checkWorkspaceRoot = false } = options;\n const flagName = command === 'add' ? '--to' : '--from';\n \n // Two arguments provided: explicit package name + path\n if (packageName && pathArg) {\n return { resolvedPackageName: packageName, resolvedPath: pathArg };\n }\n\n // One argument provided\n const singleArg = packageName || pathArg;\n if (!singleArg) {\n throw new Error(`Path argument is required for ${command}.`);\n }\n\n // Check if single arg is a valid path\n const absPath = resolvePath(cwd, singleArg);\n let pathExists = await exists(absPath);\n \n // For remove command, also check workspace root path\n if (!pathExists && checkWorkspaceRoot) {\n const openpackageDir = getLocalOpenPackageDir(cwd);\n const workspaceRootPath = join(openpackageDir, singleArg);\n pathExists = await exists(workspaceRootPath);\n }\n \n if (pathExists) {\n // It's a valid path \u2192 operate on workspace root\n return { resolvedPackageName: null, resolvedPath: singleArg };\n }\n\n // Path doesn't exist. For remove, pass through so the pipeline can try dependency resolution.\n // For add, this would have failed earlier. Only remove uses checkWorkspaceRoot.\n if (checkWorkspaceRoot && command === 'remove') {\n return { resolvedPackageName: null, resolvedPath: singleArg };\n }\n\n // Not a valid path \u2192 treat as package name (error will be thrown later)\n throw new Error(\n `Path '${singleArg}' not found.\\n\\n` +\n `If you meant to specify a package name, use: opkg ${command} <path> ${flagName} ${singleArg}`\n );\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["join", "join", "basename", "relative", "isJunk", "basename", "isJunk", "relative", "join", "join"]
|
|
7
|
+
}
|