@lousy-agents/mcp 3.1.3 → 4.0.0
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/dist/mcp-server.js +298 -149
- package/dist/mcp-server.js.map +1 -1
- package/package.json +1 -1
package/dist/mcp-server.js
CHANGED
|
@@ -29898,7 +29898,7 @@ function schemas_preprocess(fn, schema) {
|
|
|
29898
29898
|
// Zod 3 compat layer
|
|
29899
29899
|
|
|
29900
29900
|
/** @deprecated Use the raw string literal codes instead, e.g. "invalid_type". */
|
|
29901
|
-
const ZodIssueCode =
|
|
29901
|
+
const ZodIssueCode = {
|
|
29902
29902
|
invalid_type: "invalid_type",
|
|
29903
29903
|
too_big: "too_big",
|
|
29904
29904
|
too_small: "too_small",
|
|
@@ -29910,7 +29910,7 @@ const ZodIssueCode = (/* unused pure expression or super */ null && ({
|
|
|
29910
29910
|
invalid_element: "invalid_element",
|
|
29911
29911
|
invalid_value: "invalid_value",
|
|
29912
29912
|
custom: "custom",
|
|
29913
|
-
}
|
|
29913
|
+
};
|
|
29914
29914
|
|
|
29915
29915
|
/** @deprecated Use `z.config(params)` instead. */
|
|
29916
29916
|
function setErrorMap(map) {
|
|
@@ -42830,8 +42830,9 @@ var external_node_path_ = __webpack_require__(6760);
|
|
|
42830
42830
|
/**
|
|
42831
42831
|
* Shared file system utilities for gateways.
|
|
42832
42832
|
*/
|
|
42833
|
+
|
|
42833
42834
|
/**
|
|
42834
|
-
* Checks if a file or directory exists
|
|
42835
|
+
* Checks if a file or directory exists.
|
|
42835
42836
|
*/ async function file_system_utils_fileExists(path) {
|
|
42836
42837
|
try {
|
|
42837
42838
|
await (0,promises_.access)(path);
|
|
@@ -42840,6 +42841,65 @@ var external_node_path_ = __webpack_require__(6760);
|
|
|
42840
42841
|
return false;
|
|
42841
42842
|
}
|
|
42842
42843
|
}
|
|
42844
|
+
function isPathWithinRoot(rootPath, candidatePath) {
|
|
42845
|
+
return candidatePath === rootPath || candidatePath.startsWith(`${rootPath}${external_node_path_.sep}`);
|
|
42846
|
+
}
|
|
42847
|
+
/**
|
|
42848
|
+
* Resolves a relative path under targetDir and rejects traversal outside the root.
|
|
42849
|
+
*/ async function resolvePathWithinRoot(targetDir, relativePath) {
|
|
42850
|
+
if (!relativePath) {
|
|
42851
|
+
throw new Error("Path must not be empty");
|
|
42852
|
+
}
|
|
42853
|
+
const rootPath = await (0,promises_.realpath)(targetDir);
|
|
42854
|
+
const resolvedPath = (0,external_node_path_.resolve)(rootPath, relativePath);
|
|
42855
|
+
if (!isPathWithinRoot(rootPath, resolvedPath)) {
|
|
42856
|
+
throw new Error(`Resolved path is outside target directory: ${relativePath}`);
|
|
42857
|
+
}
|
|
42858
|
+
return resolvedPath;
|
|
42859
|
+
}
|
|
42860
|
+
/**
|
|
42861
|
+
* Ensures existing path segments under targetDir are not symbolic links.
|
|
42862
|
+
*/ async function assertPathHasNoSymbolicLinks(targetDir, absolutePath) {
|
|
42863
|
+
const rootPath = await (0,promises_.realpath)(targetDir);
|
|
42864
|
+
if (!isPathWithinRoot(rootPath, absolutePath)) {
|
|
42865
|
+
throw new Error(`Resolved path is outside target directory: ${absolutePath}`);
|
|
42866
|
+
}
|
|
42867
|
+
const relativePath = (0,external_node_path_.relative)(rootPath, absolutePath);
|
|
42868
|
+
if (!relativePath || relativePath === ".") {
|
|
42869
|
+
return;
|
|
42870
|
+
}
|
|
42871
|
+
const segments = relativePath.split(external_node_path_.sep);
|
|
42872
|
+
let currentPath = rootPath;
|
|
42873
|
+
for (const segment of segments){
|
|
42874
|
+
currentPath = (0,external_node_path_.join)(currentPath, segment);
|
|
42875
|
+
try {
|
|
42876
|
+
const stats = await (0,promises_.lstat)(currentPath);
|
|
42877
|
+
if (stats.isSymbolicLink()) {
|
|
42878
|
+
throw new Error(`Path contains symbolic link: ${currentPath}`);
|
|
42879
|
+
}
|
|
42880
|
+
} catch (error) {
|
|
42881
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
42882
|
+
return;
|
|
42883
|
+
}
|
|
42884
|
+
throw error;
|
|
42885
|
+
}
|
|
42886
|
+
}
|
|
42887
|
+
}
|
|
42888
|
+
/**
|
|
42889
|
+
* Resolves a relative path under targetDir and validates it does not pass through symlinks.
|
|
42890
|
+
*/ async function file_system_utils_resolveSafePath(targetDir, relativePath) {
|
|
42891
|
+
const resolvedPath = await resolvePathWithinRoot(targetDir, relativePath);
|
|
42892
|
+
await assertPathHasNoSymbolicLinks(targetDir, resolvedPath);
|
|
42893
|
+
return resolvedPath;
|
|
42894
|
+
}
|
|
42895
|
+
/**
|
|
42896
|
+
* Enforces a maximum file size before reading/parsing.
|
|
42897
|
+
*/ async function assertFileSizeWithinLimit(filePath, maxBytes, context) {
|
|
42898
|
+
const fileStats = await (0,promises_.stat)(filePath);
|
|
42899
|
+
if (fileStats.size > maxBytes) {
|
|
42900
|
+
throw new Error(`${context} exceeds size limit (${fileStats.size} bytes > ${maxBytes} bytes)`);
|
|
42901
|
+
}
|
|
42902
|
+
}
|
|
42843
42903
|
|
|
42844
42904
|
;// CONCATENATED MODULE: ../core/src/gateways/agent-file-gateway.ts
|
|
42845
42905
|
/**
|
|
@@ -42859,13 +42919,13 @@ var external_node_path_ = __webpack_require__(6760);
|
|
|
42859
42919
|
return fileExists(filePath);
|
|
42860
42920
|
}
|
|
42861
42921
|
async ensureAgentsDirectory(targetDir) {
|
|
42862
|
-
const agentsDir =
|
|
42922
|
+
const agentsDir = await resolveSafePath(targetDir, ".github/agents");
|
|
42863
42923
|
await mkdir(agentsDir, {
|
|
42864
42924
|
recursive: true
|
|
42865
42925
|
});
|
|
42866
42926
|
}
|
|
42867
42927
|
async writeAgentFile(targetDir, agentName, content) {
|
|
42868
|
-
const filePath =
|
|
42928
|
+
const filePath = await resolveSafePath(targetDir, `.github/agents/${agentName}.md`);
|
|
42869
42929
|
await writeFile(filePath, content, {
|
|
42870
42930
|
encoding: "utf-8"
|
|
42871
42931
|
});
|
|
@@ -42883,12 +42943,11 @@ var external_node_path_ = __webpack_require__(6760);
|
|
|
42883
42943
|
* Handles reading and writing .claude/settings.json and CLAUDE.md files.
|
|
42884
42944
|
*/
|
|
42885
42945
|
|
|
42886
|
-
|
|
42887
42946
|
/**
|
|
42888
42947
|
* File system implementation of ClaudeFileGateway
|
|
42889
42948
|
*/ class FileSystemClaudeFileGateway {
|
|
42890
42949
|
async readSettings(targetDir) {
|
|
42891
|
-
const settingsPath = (
|
|
42950
|
+
const settingsPath = await file_system_utils_resolveSafePath(targetDir, ".claude/settings.json");
|
|
42892
42951
|
if (!await file_system_utils_fileExists(settingsPath)) {
|
|
42893
42952
|
return null;
|
|
42894
42953
|
}
|
|
@@ -42901,8 +42960,8 @@ var external_node_path_ = __webpack_require__(6760);
|
|
|
42901
42960
|
}
|
|
42902
42961
|
}
|
|
42903
42962
|
async writeSettings(targetDir, settings) {
|
|
42904
|
-
const claudeDir = (
|
|
42905
|
-
const settingsPath = (
|
|
42963
|
+
const claudeDir = await file_system_utils_resolveSafePath(targetDir, ".claude");
|
|
42964
|
+
const settingsPath = await file_system_utils_resolveSafePath(targetDir, ".claude/settings.json");
|
|
42906
42965
|
// Ensure .claude directory exists
|
|
42907
42966
|
await (0,promises_.mkdir)(claudeDir, {
|
|
42908
42967
|
recursive: true
|
|
@@ -42912,14 +42971,14 @@ var external_node_path_ = __webpack_require__(6760);
|
|
|
42912
42971
|
await (0,promises_.writeFile)(settingsPath, content, "utf-8");
|
|
42913
42972
|
}
|
|
42914
42973
|
async readDocumentation(targetDir) {
|
|
42915
|
-
const docPath = (
|
|
42974
|
+
const docPath = await file_system_utils_resolveSafePath(targetDir, "CLAUDE.md");
|
|
42916
42975
|
if (!await file_system_utils_fileExists(docPath)) {
|
|
42917
42976
|
return null;
|
|
42918
42977
|
}
|
|
42919
42978
|
return (0,promises_.readFile)(docPath, "utf-8");
|
|
42920
42979
|
}
|
|
42921
42980
|
async writeDocumentation(targetDir, content) {
|
|
42922
|
-
const docPath = (
|
|
42981
|
+
const docPath = await file_system_utils_resolveSafePath(targetDir, "CLAUDE.md");
|
|
42923
42982
|
// Ensure content has trailing newline
|
|
42924
42983
|
const normalizedContent = content.endsWith("\n") ? content : `${content}\n`;
|
|
42925
42984
|
await (0,promises_.writeFile)(docPath, normalizedContent, "utf-8");
|
|
@@ -45169,13 +45228,27 @@ async function watchConfig(options) {
|
|
|
45169
45228
|
|
|
45170
45229
|
//#endregion
|
|
45171
45230
|
|
|
45172
|
-
;// CONCATENATED MODULE: ../core/src/
|
|
45173
|
-
/**
|
|
45174
|
-
*
|
|
45175
|
-
*
|
|
45176
|
-
*/
|
|
45231
|
+
;// CONCATENATED MODULE: ../core/src/entities/copilot-setup-config.ts
|
|
45232
|
+
/**
|
|
45233
|
+
* Canonical install command per package manager type.
|
|
45234
|
+
* Commands are intentionally fixed to a safe allowlist.
|
|
45235
|
+
*/ const PACKAGE_MANAGER_INSTALL_COMMANDS = {
|
|
45236
|
+
npm: "npm ci",
|
|
45237
|
+
yarn: "yarn install --frozen-lockfile",
|
|
45238
|
+
pnpm: "pnpm install --frozen-lockfile",
|
|
45239
|
+
pip: "pip install -r requirements.txt",
|
|
45240
|
+
pipenv: "pipenv install --deploy",
|
|
45241
|
+
poetry: "poetry install --no-root",
|
|
45242
|
+
bundler: "bundle install",
|
|
45243
|
+
cargo: "cargo build",
|
|
45244
|
+
composer: "composer install",
|
|
45245
|
+
maven: "mvn install -DskipTests",
|
|
45246
|
+
gradle: "gradle build -x test",
|
|
45247
|
+
gomod: "go mod download",
|
|
45248
|
+
pub: "dart pub get"
|
|
45249
|
+
};
|
|
45177
45250
|
/**
|
|
45178
|
-
* Default version file mappings
|
|
45251
|
+
* Default version file mappings.
|
|
45179
45252
|
*/ const DEFAULT_VERSION_FILES = [
|
|
45180
45253
|
{
|
|
45181
45254
|
filename: ".nvmrc",
|
|
@@ -45203,7 +45276,7 @@ async function watchConfig(options) {
|
|
|
45203
45276
|
}
|
|
45204
45277
|
];
|
|
45205
45278
|
/**
|
|
45206
|
-
* Default setup action configurations
|
|
45279
|
+
* Default setup action configurations.
|
|
45207
45280
|
*/ const DEFAULT_SETUP_ACTIONS = [
|
|
45208
45281
|
{
|
|
45209
45282
|
action: "actions/setup-node",
|
|
@@ -45232,7 +45305,7 @@ async function watchConfig(options) {
|
|
|
45232
45305
|
}
|
|
45233
45306
|
];
|
|
45234
45307
|
/**
|
|
45235
|
-
* Default setup action patterns to detect in workflows
|
|
45308
|
+
* Default setup action patterns to detect in workflows.
|
|
45236
45309
|
*/ const DEFAULT_SETUP_ACTION_PATTERNS = [
|
|
45237
45310
|
"actions/setup-node",
|
|
45238
45311
|
"actions/setup-python",
|
|
@@ -45242,112 +45315,212 @@ async function watchConfig(options) {
|
|
|
45242
45315
|
"jdx/mise-action"
|
|
45243
45316
|
];
|
|
45244
45317
|
/**
|
|
45245
|
-
* Default package manager mappings
|
|
45246
|
-
* Based on Dependabot supported ecosystems
|
|
45318
|
+
* Default package manager mappings based on common ecosystems.
|
|
45247
45319
|
*/ const DEFAULT_PACKAGE_MANAGERS = [
|
|
45248
|
-
// Node.js package managers
|
|
45249
45320
|
{
|
|
45250
45321
|
type: "npm",
|
|
45251
45322
|
manifestFile: "package.json",
|
|
45252
45323
|
lockfile: "package-lock.json",
|
|
45253
|
-
installCommand:
|
|
45324
|
+
installCommand: PACKAGE_MANAGER_INSTALL_COMMANDS.npm
|
|
45254
45325
|
},
|
|
45255
45326
|
{
|
|
45256
45327
|
type: "yarn",
|
|
45257
45328
|
manifestFile: "package.json",
|
|
45258
45329
|
lockfile: "yarn.lock",
|
|
45259
|
-
installCommand:
|
|
45330
|
+
installCommand: PACKAGE_MANAGER_INSTALL_COMMANDS.yarn
|
|
45260
45331
|
},
|
|
45261
45332
|
{
|
|
45262
45333
|
type: "pnpm",
|
|
45263
45334
|
manifestFile: "package.json",
|
|
45264
45335
|
lockfile: "pnpm-lock.yaml",
|
|
45265
|
-
installCommand:
|
|
45336
|
+
installCommand: PACKAGE_MANAGER_INSTALL_COMMANDS.pnpm
|
|
45266
45337
|
},
|
|
45267
|
-
// Python package managers
|
|
45268
45338
|
{
|
|
45269
45339
|
type: "pip",
|
|
45270
45340
|
manifestFile: "requirements.txt",
|
|
45271
|
-
installCommand:
|
|
45341
|
+
installCommand: PACKAGE_MANAGER_INSTALL_COMMANDS.pip
|
|
45272
45342
|
},
|
|
45273
45343
|
{
|
|
45274
45344
|
type: "pipenv",
|
|
45275
45345
|
manifestFile: "Pipfile",
|
|
45276
45346
|
lockfile: "Pipfile.lock",
|
|
45277
|
-
installCommand:
|
|
45347
|
+
installCommand: PACKAGE_MANAGER_INSTALL_COMMANDS.pipenv
|
|
45278
45348
|
},
|
|
45279
45349
|
{
|
|
45280
45350
|
type: "poetry",
|
|
45281
45351
|
manifestFile: "pyproject.toml",
|
|
45282
45352
|
lockfile: "poetry.lock",
|
|
45283
45353
|
requiresLockfile: true,
|
|
45284
|
-
installCommand:
|
|
45354
|
+
installCommand: PACKAGE_MANAGER_INSTALL_COMMANDS.poetry
|
|
45285
45355
|
},
|
|
45286
|
-
// Ruby
|
|
45287
45356
|
{
|
|
45288
45357
|
type: "bundler",
|
|
45289
45358
|
manifestFile: "Gemfile",
|
|
45290
45359
|
lockfile: "Gemfile.lock",
|
|
45291
|
-
installCommand:
|
|
45360
|
+
installCommand: PACKAGE_MANAGER_INSTALL_COMMANDS.bundler
|
|
45292
45361
|
},
|
|
45293
|
-
// Rust
|
|
45294
45362
|
{
|
|
45295
45363
|
type: "cargo",
|
|
45296
45364
|
manifestFile: "Cargo.toml",
|
|
45297
45365
|
lockfile: "Cargo.lock",
|
|
45298
|
-
installCommand:
|
|
45366
|
+
installCommand: PACKAGE_MANAGER_INSTALL_COMMANDS.cargo
|
|
45299
45367
|
},
|
|
45300
|
-
// PHP
|
|
45301
45368
|
{
|
|
45302
45369
|
type: "composer",
|
|
45303
45370
|
manifestFile: "composer.json",
|
|
45304
45371
|
lockfile: "composer.lock",
|
|
45305
|
-
installCommand:
|
|
45372
|
+
installCommand: PACKAGE_MANAGER_INSTALL_COMMANDS.composer
|
|
45306
45373
|
},
|
|
45307
|
-
// Java
|
|
45308
45374
|
{
|
|
45309
45375
|
type: "maven",
|
|
45310
45376
|
manifestFile: "pom.xml",
|
|
45311
|
-
installCommand:
|
|
45377
|
+
installCommand: PACKAGE_MANAGER_INSTALL_COMMANDS.maven
|
|
45312
45378
|
},
|
|
45313
45379
|
{
|
|
45314
45380
|
type: "gradle",
|
|
45315
45381
|
manifestFile: "build.gradle",
|
|
45316
|
-
installCommand:
|
|
45382
|
+
installCommand: PACKAGE_MANAGER_INSTALL_COMMANDS.gradle
|
|
45317
45383
|
},
|
|
45318
|
-
// Go
|
|
45319
45384
|
{
|
|
45320
45385
|
type: "gomod",
|
|
45321
45386
|
manifestFile: "go.mod",
|
|
45322
45387
|
lockfile: "go.sum",
|
|
45323
|
-
installCommand:
|
|
45388
|
+
installCommand: PACKAGE_MANAGER_INSTALL_COMMANDS.gomod
|
|
45324
45389
|
},
|
|
45325
|
-
// Dart/Flutter
|
|
45326
45390
|
{
|
|
45327
45391
|
type: "pub",
|
|
45328
45392
|
manifestFile: "pubspec.yaml",
|
|
45329
45393
|
lockfile: "pubspec.lock",
|
|
45330
|
-
installCommand:
|
|
45394
|
+
installCommand: PACKAGE_MANAGER_INSTALL_COMMANDS.pub
|
|
45331
45395
|
}
|
|
45332
45396
|
];
|
|
45333
45397
|
/**
|
|
45334
|
-
* Default copilot-setup configuration
|
|
45335
|
-
*/ const
|
|
45398
|
+
* Default copilot-setup configuration.
|
|
45399
|
+
*/ const DEFAULT_COPILOT_SETUP_CONFIG = {
|
|
45336
45400
|
versionFiles: DEFAULT_VERSION_FILES,
|
|
45337
45401
|
setupActions: DEFAULT_SETUP_ACTIONS,
|
|
45338
45402
|
setupActionPatterns: DEFAULT_SETUP_ACTION_PATTERNS,
|
|
45339
45403
|
packageManagers: DEFAULT_PACKAGE_MANAGERS
|
|
45340
45404
|
};
|
|
45405
|
+
|
|
45406
|
+
;// CONCATENATED MODULE: ../core/src/lib/copilot-setup-config.ts
|
|
45407
|
+
/**
|
|
45408
|
+
* Configuration for the copilot-setup command.
|
|
45409
|
+
* Uses c12 for configuration loading, allowing runtime customization.
|
|
45410
|
+
*/
|
|
45411
|
+
|
|
45412
|
+
|
|
45413
|
+
const SAFE_BASENAME_PATTERN = /^[A-Za-z0-9._-]+$/;
|
|
45414
|
+
const SAFE_ACTION_PATTERN = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/;
|
|
45415
|
+
const SAFE_VERSION_KEY_PATTERN = /^[a-z][a-z0-9-]*$/;
|
|
45416
|
+
const MAX_FILENAME_LENGTH = 255;
|
|
45417
|
+
const VERSION_FILE_TYPES = [
|
|
45418
|
+
"node",
|
|
45419
|
+
"python",
|
|
45420
|
+
"java",
|
|
45421
|
+
"ruby",
|
|
45422
|
+
"go"
|
|
45423
|
+
];
|
|
45424
|
+
const PACKAGE_MANAGER_TYPES = [
|
|
45425
|
+
"npm",
|
|
45426
|
+
"yarn",
|
|
45427
|
+
"pnpm",
|
|
45428
|
+
"pip",
|
|
45429
|
+
"pipenv",
|
|
45430
|
+
"poetry",
|
|
45431
|
+
"bundler",
|
|
45432
|
+
"cargo",
|
|
45433
|
+
"composer",
|
|
45434
|
+
"maven",
|
|
45435
|
+
"gradle",
|
|
45436
|
+
"gomod",
|
|
45437
|
+
"pub"
|
|
45438
|
+
];
|
|
45439
|
+
function isSafeBasename(value) {
|
|
45440
|
+
return value.length > 0 && value.length <= MAX_FILENAME_LENGTH && SAFE_BASENAME_PATTERN.test(value) && !value.includes("..");
|
|
45441
|
+
}
|
|
45442
|
+
const VersionFileMappingSchema = schemas_object({
|
|
45443
|
+
filename: schemas_string(),
|
|
45444
|
+
type: schemas_enum(VERSION_FILE_TYPES)
|
|
45445
|
+
}).strict().superRefine((value, context)=>{
|
|
45446
|
+
if (!isSafeBasename(value.filename)) {
|
|
45447
|
+
context.addIssue({
|
|
45448
|
+
code: ZodIssueCode.custom,
|
|
45449
|
+
path: [
|
|
45450
|
+
"filename"
|
|
45451
|
+
],
|
|
45452
|
+
message: "versionFiles.filename must be a safe basename without traversal or directory separators"
|
|
45453
|
+
});
|
|
45454
|
+
}
|
|
45455
|
+
});
|
|
45456
|
+
const SetupActionConfigSchema = schemas_object({
|
|
45457
|
+
action: schemas_string().regex(SAFE_ACTION_PATTERN, {
|
|
45458
|
+
message: "setupActions.action must be in owner/repo format"
|
|
45459
|
+
}),
|
|
45460
|
+
type: schemas_enum(VERSION_FILE_TYPES),
|
|
45461
|
+
versionFileKey: schemas_string().regex(SAFE_VERSION_KEY_PATTERN, {
|
|
45462
|
+
message: "setupActions.versionFileKey must use lowercase kebab-case"
|
|
45463
|
+
})
|
|
45464
|
+
}).strict();
|
|
45465
|
+
const PackageManagerMappingSchema = schemas_object({
|
|
45466
|
+
type: schemas_enum(PACKAGE_MANAGER_TYPES),
|
|
45467
|
+
manifestFile: schemas_string(),
|
|
45468
|
+
lockfile: schemas_string().optional(),
|
|
45469
|
+
requiresLockfile: schemas_boolean().optional(),
|
|
45470
|
+
installCommand: schemas_string()
|
|
45471
|
+
}).strict().superRefine((value, context)=>{
|
|
45472
|
+
if (!isSafeBasename(value.manifestFile)) {
|
|
45473
|
+
context.addIssue({
|
|
45474
|
+
code: ZodIssueCode.custom,
|
|
45475
|
+
path: [
|
|
45476
|
+
"manifestFile"
|
|
45477
|
+
],
|
|
45478
|
+
message: "packageManagers.manifestFile must be a safe basename without traversal or directory separators"
|
|
45479
|
+
});
|
|
45480
|
+
}
|
|
45481
|
+
if (value.lockfile && !isSafeBasename(value.lockfile)) {
|
|
45482
|
+
context.addIssue({
|
|
45483
|
+
code: ZodIssueCode.custom,
|
|
45484
|
+
path: [
|
|
45485
|
+
"lockfile"
|
|
45486
|
+
],
|
|
45487
|
+
message: "packageManagers.lockfile must be a safe basename without traversal or directory separators"
|
|
45488
|
+
});
|
|
45489
|
+
}
|
|
45490
|
+
const expectedInstallCommand = PACKAGE_MANAGER_INSTALL_COMMANDS[value.type];
|
|
45491
|
+
if (value.installCommand !== expectedInstallCommand) {
|
|
45492
|
+
context.addIssue({
|
|
45493
|
+
code: ZodIssueCode.custom,
|
|
45494
|
+
path: [
|
|
45495
|
+
"installCommand"
|
|
45496
|
+
],
|
|
45497
|
+
message: `packageManagers.installCommand for ${value.type} must be '${expectedInstallCommand}'`
|
|
45498
|
+
});
|
|
45499
|
+
}
|
|
45500
|
+
});
|
|
45501
|
+
const CopilotSetupConfigSchema = schemas_object({
|
|
45502
|
+
versionFiles: schemas_array(VersionFileMappingSchema).min(1),
|
|
45503
|
+
setupActions: schemas_array(SetupActionConfigSchema).min(1),
|
|
45504
|
+
setupActionPatterns: schemas_array(schemas_string().regex(SAFE_ACTION_PATTERN, {
|
|
45505
|
+
message: "setupActionPatterns entries must be in owner/repo format"
|
|
45506
|
+
})).min(1),
|
|
45507
|
+
packageManagers: schemas_array(PackageManagerMappingSchema).min(1)
|
|
45508
|
+
});
|
|
45509
|
+
/**
|
|
45510
|
+
* Validates loaded copilot-setup configuration and rejects unsafe values.
|
|
45511
|
+
*/ function validateCopilotSetupConfig(config) {
|
|
45512
|
+
return CopilotSetupConfigSchema.parse(config);
|
|
45513
|
+
}
|
|
45341
45514
|
/**
|
|
45342
|
-
* Loads the copilot-setup configuration using c12
|
|
45343
|
-
* Falls back to defaults if no configuration is found
|
|
45515
|
+
* Loads the copilot-setup configuration using c12.
|
|
45516
|
+
* Falls back to defaults if no configuration is found.
|
|
45344
45517
|
*/ async function loadCopilotSetupConfig() {
|
|
45345
45518
|
const { config } = await loadConfig({
|
|
45346
45519
|
name: "lousy-agents",
|
|
45347
|
-
defaults:
|
|
45520
|
+
defaults: DEFAULT_COPILOT_SETUP_CONFIG,
|
|
45348
45521
|
packageJson: "copilotSetup"
|
|
45349
45522
|
});
|
|
45350
|
-
return config
|
|
45523
|
+
return validateCopilotSetupConfig(config ?? DEFAULT_COPILOT_SETUP_CONFIG);
|
|
45351
45524
|
}
|
|
45352
45525
|
/**
|
|
45353
45526
|
* Resets the cached configuration (useful for testing)
|
|
@@ -45358,8 +45531,8 @@ async function watchConfig(options) {
|
|
|
45358
45531
|
// no in-memory cache to reset
|
|
45359
45532
|
}
|
|
45360
45533
|
/**
|
|
45361
|
-
* Gets a version file type to action mapping from config
|
|
45362
|
-
* Returns a partial record as not all types may be configured
|
|
45534
|
+
* Gets a version file type to action mapping from config.
|
|
45535
|
+
* Returns a partial record as not all types may be configured.
|
|
45363
45536
|
*/ function getVersionTypeToActionMap(config) {
|
|
45364
45537
|
const map = {};
|
|
45365
45538
|
for (const action of config.setupActions){
|
|
@@ -45368,8 +45541,8 @@ async function watchConfig(options) {
|
|
|
45368
45541
|
return map;
|
|
45369
45542
|
}
|
|
45370
45543
|
/**
|
|
45371
|
-
* Gets a version file type to config key mapping from config
|
|
45372
|
-
* Returns a partial record as not all types may be configured
|
|
45544
|
+
* Gets a version file type to config key mapping from config.
|
|
45545
|
+
* Returns a partial record as not all types may be configured.
|
|
45373
45546
|
*/ function getVersionFileConfigKeyMap(config) {
|
|
45374
45547
|
const map = {};
|
|
45375
45548
|
for (const action of config.setupActions){
|
|
@@ -45378,7 +45551,7 @@ async function watchConfig(options) {
|
|
|
45378
45551
|
return map;
|
|
45379
45552
|
}
|
|
45380
45553
|
/**
|
|
45381
|
-
* Gets a filename to version type mapping from config
|
|
45554
|
+
* Gets a filename to version type mapping from config.
|
|
45382
45555
|
*/ function getVersionFilenameToTypeMap(config) {
|
|
45383
45556
|
const map = {};
|
|
45384
45557
|
for (const file of config.versionFiles){
|
|
@@ -45395,15 +45568,14 @@ async function watchConfig(options) {
|
|
|
45395
45568
|
|
|
45396
45569
|
|
|
45397
45570
|
|
|
45398
|
-
|
|
45399
|
-
|
|
45400
|
-
|
|
45401
|
-
*/ async function readVersionFileContent(filePath) {
|
|
45571
|
+
const MAX_VERSION_FILE_BYTES = 16 * 1024;
|
|
45572
|
+
async function readVersionFileContent(filePath) {
|
|
45573
|
+
await assertFileSizeWithinLimit(filePath, MAX_VERSION_FILE_BYTES, `Version file '${filePath}'`);
|
|
45402
45574
|
const content = await (0,promises_.readFile)(filePath, "utf-8");
|
|
45403
45575
|
return content.trim();
|
|
45404
45576
|
}
|
|
45405
45577
|
/**
|
|
45406
|
-
* File system implementation of the environment gateway
|
|
45578
|
+
* File system implementation of the environment gateway.
|
|
45407
45579
|
*/ class FileSystemEnvironmentGateway {
|
|
45408
45580
|
config = null;
|
|
45409
45581
|
async getConfig() {
|
|
@@ -45424,14 +45596,14 @@ async function watchConfig(options) {
|
|
|
45424
45596
|
};
|
|
45425
45597
|
}
|
|
45426
45598
|
async detectMise(targetDir) {
|
|
45427
|
-
const miseTomlPath = (
|
|
45599
|
+
const miseTomlPath = await file_system_utils_resolveSafePath(targetDir, "mise.toml");
|
|
45428
45600
|
return file_system_utils_fileExists(miseTomlPath);
|
|
45429
45601
|
}
|
|
45430
45602
|
async detectVersionFiles(targetDir, config) {
|
|
45431
45603
|
const filenameToType = getVersionFilenameToTypeMap(config);
|
|
45432
45604
|
const versionFiles = [];
|
|
45433
45605
|
for (const fileConfig of config.versionFiles){
|
|
45434
|
-
const filePath = (
|
|
45606
|
+
const filePath = await file_system_utils_resolveSafePath(targetDir, fileConfig.filename);
|
|
45435
45607
|
if (await file_system_utils_fileExists(filePath)) {
|
|
45436
45608
|
const version = await readVersionFileContent(filePath);
|
|
45437
45609
|
versionFiles.push({
|
|
@@ -45445,32 +45617,27 @@ async function watchConfig(options) {
|
|
|
45445
45617
|
}
|
|
45446
45618
|
async detectPackageManagers(targetDir, config) {
|
|
45447
45619
|
const packageManagers = [];
|
|
45448
|
-
// Helper to check if a package manager type is in a list
|
|
45449
45620
|
const isPackageManagerType = (pm, types)=>types.includes(pm.type);
|
|
45450
45621
|
const nodePackageManagers = config.packageManagers.filter((pm)=>isPackageManagerType(pm, NODE_PACKAGE_MANAGERS));
|
|
45451
45622
|
const pythonPackageManagers = config.packageManagers.filter((pm)=>isPackageManagerType(pm, PYTHON_PACKAGE_MANAGERS));
|
|
45452
45623
|
const otherPackageManagers = config.packageManagers.filter((pm)=>!isPackageManagerType(pm, NODE_PACKAGE_MANAGERS) && !isPackageManagerType(pm, PYTHON_PACKAGE_MANAGERS));
|
|
45453
|
-
// Detect Node.js package manager (with prioritization)
|
|
45454
45624
|
const nodePackageManager = await this.detectNodePackageManager(targetDir, nodePackageManagers);
|
|
45455
45625
|
if (nodePackageManager) {
|
|
45456
45626
|
packageManagers.push(nodePackageManager);
|
|
45457
45627
|
}
|
|
45458
|
-
// Detect Python package manager (with prioritization)
|
|
45459
45628
|
const pythonPackageManager = await this.detectPythonPackageManager(targetDir, pythonPackageManagers);
|
|
45460
45629
|
if (pythonPackageManager) {
|
|
45461
45630
|
packageManagers.push(pythonPackageManager);
|
|
45462
45631
|
}
|
|
45463
|
-
// Detect other package managers
|
|
45464
45632
|
const otherDetectedManagers = await this.detectOtherPackageManagers(targetDir, otherPackageManagers);
|
|
45465
45633
|
packageManagers.push(...otherDetectedManagers);
|
|
45466
45634
|
return packageManagers;
|
|
45467
45635
|
}
|
|
45468
45636
|
async detectNodePackageManager(targetDir, nodePackageManagers) {
|
|
45469
|
-
const packageJsonPath = (
|
|
45637
|
+
const packageJsonPath = await file_system_utils_resolveSafePath(targetDir, "package.json");
|
|
45470
45638
|
if (!await file_system_utils_fileExists(packageJsonPath)) {
|
|
45471
45639
|
return null;
|
|
45472
45640
|
}
|
|
45473
|
-
// Priority order for Node.js package managers: npm > yarn > pnpm
|
|
45474
45641
|
const lockfileOrder = [
|
|
45475
45642
|
"npm",
|
|
45476
45643
|
"yarn",
|
|
@@ -45481,7 +45648,7 @@ async function watchConfig(options) {
|
|
|
45481
45648
|
if (!pmConfig?.lockfile) {
|
|
45482
45649
|
continue;
|
|
45483
45650
|
}
|
|
45484
|
-
const lockfilePath = (
|
|
45651
|
+
const lockfilePath = await file_system_utils_resolveSafePath(targetDir, pmConfig.lockfile);
|
|
45485
45652
|
if (await file_system_utils_fileExists(lockfilePath)) {
|
|
45486
45653
|
return {
|
|
45487
45654
|
type: pmConfig.type,
|
|
@@ -45490,7 +45657,6 @@ async function watchConfig(options) {
|
|
|
45490
45657
|
};
|
|
45491
45658
|
}
|
|
45492
45659
|
}
|
|
45493
|
-
// Default to npm if no lockfile found
|
|
45494
45660
|
const npmConfig = nodePackageManagers.find((pm)=>pm.type === "npm");
|
|
45495
45661
|
if (npmConfig) {
|
|
45496
45662
|
return {
|
|
@@ -45502,17 +45668,15 @@ async function watchConfig(options) {
|
|
|
45502
45668
|
return null;
|
|
45503
45669
|
}
|
|
45504
45670
|
async detectPythonPackageManager(targetDir, pythonPackageManagers) {
|
|
45505
|
-
// Priority order for Python package managers: poetry > pipenv > pip
|
|
45506
45671
|
for (const pmType of PYTHON_PACKAGE_MANAGERS){
|
|
45507
45672
|
const pmConfig = pythonPackageManagers.find((pm)=>pm.type === pmType);
|
|
45508
45673
|
if (!pmConfig) {
|
|
45509
45674
|
continue;
|
|
45510
45675
|
}
|
|
45511
|
-
const manifestPath = (
|
|
45676
|
+
const manifestPath = await file_system_utils_resolveSafePath(targetDir, pmConfig.manifestFile);
|
|
45512
45677
|
if (await file_system_utils_fileExists(manifestPath)) {
|
|
45513
|
-
const lockfilePath = pmConfig.lockfile ? (
|
|
45678
|
+
const lockfilePath = pmConfig.lockfile ? await file_system_utils_resolveSafePath(targetDir, pmConfig.lockfile) : undefined;
|
|
45514
45679
|
const hasLockfile = lockfilePath ? await file_system_utils_fileExists(lockfilePath) : false;
|
|
45515
|
-
// Skip if lockfile is required but not present
|
|
45516
45680
|
if (pmConfig.requiresLockfile && !hasLockfile) {
|
|
45517
45681
|
continue;
|
|
45518
45682
|
}
|
|
@@ -45528,11 +45692,10 @@ async function watchConfig(options) {
|
|
|
45528
45692
|
async detectOtherPackageManagers(targetDir, otherPackageManagers) {
|
|
45529
45693
|
const packageManagers = [];
|
|
45530
45694
|
for (const pmConfig of otherPackageManagers){
|
|
45531
|
-
const manifestPath = (
|
|
45695
|
+
const manifestPath = await file_system_utils_resolveSafePath(targetDir, pmConfig.manifestFile);
|
|
45532
45696
|
if (await file_system_utils_fileExists(manifestPath)) {
|
|
45533
|
-
const lockfilePath = pmConfig.lockfile ? (
|
|
45697
|
+
const lockfilePath = pmConfig.lockfile ? await file_system_utils_resolveSafePath(targetDir, pmConfig.lockfile) : undefined;
|
|
45534
45698
|
const hasLockfile = lockfilePath ? await file_system_utils_fileExists(lockfilePath) : false;
|
|
45535
|
-
// Skip if lockfile is required but not present
|
|
45536
45699
|
if (pmConfig.requiresLockfile && !hasLockfile) {
|
|
45537
45700
|
continue;
|
|
45538
45701
|
}
|
|
@@ -45547,7 +45710,7 @@ async function watchConfig(options) {
|
|
|
45547
45710
|
}
|
|
45548
45711
|
}
|
|
45549
45712
|
/**
|
|
45550
|
-
* Creates and returns the default environment gateway
|
|
45713
|
+
* Creates and returns the default environment gateway.
|
|
45551
45714
|
*/ function createEnvironmentGateway() {
|
|
45552
45715
|
return new FileSystemEnvironmentGateway();
|
|
45553
45716
|
}
|
|
@@ -46041,13 +46204,13 @@ function defaultExec(command, args, options) {
|
|
|
46041
46204
|
return fileExists(dirPath);
|
|
46042
46205
|
}
|
|
46043
46206
|
async ensureSkillDirectory(targetDir, skillName) {
|
|
46044
|
-
const skillDir =
|
|
46207
|
+
const skillDir = await resolveSafePath(targetDir, `.github/skills/${skillName}`);
|
|
46045
46208
|
await mkdir(skillDir, {
|
|
46046
46209
|
recursive: true
|
|
46047
46210
|
});
|
|
46048
46211
|
}
|
|
46049
46212
|
async writeSkillFile(targetDir, skillName, content) {
|
|
46050
|
-
const filePath =
|
|
46213
|
+
const filePath = await resolveSafePath(targetDir, `.github/skills/${skillName}/SKILL.md`);
|
|
46051
46214
|
await writeFile(filePath, content, {
|
|
46052
46215
|
encoding: "utf-8"
|
|
46053
46216
|
});
|
|
@@ -46497,17 +46660,12 @@ var yaml_dist = __webpack_require__(3519);
|
|
|
46497
46660
|
|
|
46498
46661
|
|
|
46499
46662
|
|
|
46500
|
-
|
|
46501
|
-
/**
|
|
46502
|
-
* Possible filenames for the Copilot Setup Steps workflow.
|
|
46503
|
-
* Supports both .yml and .yaml extensions.
|
|
46504
|
-
*/ const COPILOT_SETUP_WORKFLOW_FILENAMES = [
|
|
46663
|
+
const COPILOT_SETUP_WORKFLOW_FILENAMES = [
|
|
46505
46664
|
"copilot-setup-steps.yml",
|
|
46506
46665
|
"copilot-setup-steps.yaml"
|
|
46507
46666
|
];
|
|
46508
|
-
|
|
46509
|
-
|
|
46510
|
-
*/ class FileSystemWorkflowGateway {
|
|
46667
|
+
const MAX_WORKFLOW_FILE_BYTES = 1024 * 1024;
|
|
46668
|
+
class FileSystemWorkflowGateway {
|
|
46511
46669
|
config = null;
|
|
46512
46670
|
async getConfig() {
|
|
46513
46671
|
if (!this.config) {
|
|
@@ -46515,13 +46673,9 @@ var yaml_dist = __webpack_require__(3519);
|
|
|
46515
46673
|
}
|
|
46516
46674
|
return this.config;
|
|
46517
46675
|
}
|
|
46518
|
-
|
|
46519
|
-
* Finds the path to an existing Copilot Setup Steps workflow file.
|
|
46520
|
-
* @returns The full path if found, or null if no workflow exists.
|
|
46521
|
-
*/ async findCopilotSetupWorkflowPath(targetDir) {
|
|
46522
|
-
const workflowsDir = (0,external_node_path_.join)(targetDir, ".github", "workflows");
|
|
46676
|
+
async findCopilotSetupWorkflowPath(targetDir) {
|
|
46523
46677
|
for (const filename of COPILOT_SETUP_WORKFLOW_FILENAMES){
|
|
46524
|
-
const workflowPath = (
|
|
46678
|
+
const workflowPath = await file_system_utils_resolveSafePath(targetDir, `.github/workflows/${filename}`);
|
|
46525
46679
|
if (await file_system_utils_fileExists(workflowPath)) {
|
|
46526
46680
|
return workflowPath;
|
|
46527
46681
|
}
|
|
@@ -46529,7 +46683,7 @@ var yaml_dist = __webpack_require__(3519);
|
|
|
46529
46683
|
return null;
|
|
46530
46684
|
}
|
|
46531
46685
|
async parseWorkflowsForSetupActions(targetDir) {
|
|
46532
|
-
const workflowsDir = (
|
|
46686
|
+
const workflowsDir = await file_system_utils_resolveSafePath(targetDir, ".github/workflows");
|
|
46533
46687
|
if (!await file_system_utils_fileExists(workflowsDir)) {
|
|
46534
46688
|
return [];
|
|
46535
46689
|
}
|
|
@@ -46538,15 +46692,15 @@ var yaml_dist = __webpack_require__(3519);
|
|
|
46538
46692
|
const config = await this.getConfig();
|
|
46539
46693
|
const allCandidates = [];
|
|
46540
46694
|
for (const file of yamlFiles){
|
|
46541
|
-
const filePath = (
|
|
46695
|
+
const filePath = await file_system_utils_resolveSafePath(targetDir, `.github/workflows/${file}`);
|
|
46696
|
+
await assertFileSizeWithinLimit(filePath, MAX_WORKFLOW_FILE_BYTES, `Workflow file '${file}'`);
|
|
46542
46697
|
try {
|
|
46543
46698
|
const content = await (0,promises_.readFile)(filePath, "utf-8");
|
|
46544
46699
|
const workflow = (0,yaml_dist.parse)(content);
|
|
46545
46700
|
const candidates = extractSetupStepsFromWorkflow(workflow, config.setupActionPatterns);
|
|
46546
46701
|
allCandidates.push(...candidates);
|
|
46547
46702
|
} catch {
|
|
46548
|
-
// Skip
|
|
46549
|
-
// This is expected for malformed or non-workflow YAML files
|
|
46703
|
+
// Skip unreadable or malformed YAML workflow files while continuing scan.
|
|
46550
46704
|
}
|
|
46551
46705
|
}
|
|
46552
46706
|
return deduplicateCandidates(allCandidates);
|
|
@@ -46557,27 +46711,27 @@ var yaml_dist = __webpack_require__(3519);
|
|
|
46557
46711
|
}
|
|
46558
46712
|
async getCopilotSetupWorkflowPath(targetDir) {
|
|
46559
46713
|
const existingPath = await this.findCopilotSetupWorkflowPath(targetDir);
|
|
46560
|
-
|
|
46714
|
+
if (existingPath) {
|
|
46715
|
+
return existingPath;
|
|
46716
|
+
}
|
|
46717
|
+
return file_system_utils_resolveSafePath(targetDir, ".github/workflows/copilot-setup-steps.yml");
|
|
46561
46718
|
}
|
|
46562
46719
|
async readCopilotSetupWorkflow(targetDir) {
|
|
46563
46720
|
const workflowPath = await this.findCopilotSetupWorkflowPath(targetDir);
|
|
46564
46721
|
if (!workflowPath) {
|
|
46565
46722
|
return null;
|
|
46566
46723
|
}
|
|
46724
|
+
await assertFileSizeWithinLimit(workflowPath, MAX_WORKFLOW_FILE_BYTES, "Copilot setup workflow");
|
|
46567
46725
|
const content = await (0,promises_.readFile)(workflowPath, "utf-8");
|
|
46568
46726
|
return (0,yaml_dist.parse)(content);
|
|
46569
46727
|
}
|
|
46570
46728
|
async writeCopilotSetupWorkflow(targetDir, content) {
|
|
46571
|
-
// Check if an existing workflow file exists (either extension)
|
|
46572
46729
|
const existingPath = await this.findCopilotSetupWorkflowPath(targetDir);
|
|
46573
|
-
|
|
46574
|
-
const workflowPath = existingPath || (0,external_node_path_.join)(targetDir, ".github", "workflows", "copilot-setup-steps.yml");
|
|
46730
|
+
const workflowPath = existingPath ?? await file_system_utils_resolveSafePath(targetDir, ".github/workflows/copilot-setup-steps.yml");
|
|
46575
46731
|
await (0,promises_.writeFile)(workflowPath, content, "utf-8");
|
|
46576
46732
|
}
|
|
46577
46733
|
}
|
|
46578
|
-
|
|
46579
|
-
* Creates and returns the default workflow gateway
|
|
46580
|
-
*/ function createWorkflowGateway() {
|
|
46734
|
+
function createWorkflowGateway() {
|
|
46581
46735
|
return new FileSystemWorkflowGateway();
|
|
46582
46736
|
}
|
|
46583
46737
|
|
|
@@ -66216,8 +66370,7 @@ const remark = unified().use(remarkParse).use(remarkStringify).freeze()
|
|
|
66216
66370
|
* @param environment The detected environment configuration
|
|
66217
66371
|
* @param config Optional copilot-setup configuration (for package manager mappings)
|
|
66218
66372
|
* @returns Array of SessionStart hooks
|
|
66219
|
-
*/ async function buildSessionStartHooks(environment, config) {
|
|
66220
|
-
const loadedConfig = config || await loadCopilotSetupConfig();
|
|
66373
|
+
*/ async function buildSessionStartHooks(environment, config = DEFAULT_COPILOT_SETUP_CONFIG) {
|
|
66221
66374
|
const hooks = [];
|
|
66222
66375
|
// If mise.toml is present, add mise install
|
|
66223
66376
|
if (environment.hasMise) {
|
|
@@ -66226,7 +66379,7 @@ const remark = unified().use(remarkParse).use(remarkStringify).freeze()
|
|
|
66226
66379
|
description: "Install runtimes from mise.toml"
|
|
66227
66380
|
});
|
|
66228
66381
|
// After mise install, add package manager install hooks
|
|
66229
|
-
const packageManagerHooks = buildPackageManagerHooks(environment.packageManagers,
|
|
66382
|
+
const packageManagerHooks = buildPackageManagerHooks(environment.packageManagers, config);
|
|
66230
66383
|
hooks.push(...packageManagerHooks);
|
|
66231
66384
|
return hooks;
|
|
66232
66385
|
}
|
|
@@ -66234,7 +66387,7 @@ const remark = unified().use(remarkParse).use(remarkStringify).freeze()
|
|
|
66234
66387
|
const runtimeHooks = buildRuntimeHooks(environment.versionFiles);
|
|
66235
66388
|
hooks.push(...runtimeHooks);
|
|
66236
66389
|
// Add package manager install hooks
|
|
66237
|
-
const packageManagerHooks = buildPackageManagerHooks(environment.packageManagers,
|
|
66390
|
+
const packageManagerHooks = buildPackageManagerHooks(environment.packageManagers, config);
|
|
66238
66391
|
hooks.push(...packageManagerHooks);
|
|
66239
66392
|
return hooks;
|
|
66240
66393
|
}
|
|
@@ -66504,6 +66657,7 @@ const remark = unified().use(remarkParse).use(remarkStringify).freeze()
|
|
|
66504
66657
|
|
|
66505
66658
|
|
|
66506
66659
|
|
|
66660
|
+
|
|
66507
66661
|
/**
|
|
66508
66662
|
* Creates or updates Claude Code web environment setup files.
|
|
66509
66663
|
*/ const createClaudeCodeWebSetupHandler = async (args)=>{
|
|
@@ -66515,8 +66669,9 @@ const remark = unified().use(remarkParse).use(remarkStringify).freeze()
|
|
|
66515
66669
|
const claudeGateway = createClaudeFileGateway();
|
|
66516
66670
|
// Detect environment configuration
|
|
66517
66671
|
const environment = await environmentGateway.detectEnvironment(dir);
|
|
66672
|
+
const copilotSetupConfig = await loadCopilotSetupConfig();
|
|
66518
66673
|
// Build SessionStart hooks from environment
|
|
66519
|
-
const hooks = await buildSessionStartHooks(environment);
|
|
66674
|
+
const hooks = await buildSessionStartHooks(environment, copilotSetupConfig);
|
|
66520
66675
|
// Read existing settings
|
|
66521
66676
|
const existingSettings = await claudeGateway.readSettings(dir);
|
|
66522
66677
|
// Merge settings
|
|
@@ -66728,17 +66883,29 @@ Example resolved format: actions/setup-node@1a2b3c4d5e6f # v4.0.0`;
|
|
|
66728
66883
|
*/
|
|
66729
66884
|
|
|
66730
66885
|
/**
|
|
66731
|
-
*
|
|
66732
|
-
|
|
66733
|
-
|
|
66734
|
-
|
|
66735
|
-
|
|
66736
|
-
|
|
66737
|
-
|
|
66738
|
-
|
|
66739
|
-
|
|
66886
|
+
* Creates an ActionVersionPort backed by a static version map.
|
|
66887
|
+
*/ function createActionVersionPort(versionMap) {
|
|
66888
|
+
return {
|
|
66889
|
+
async getVersion (actionName) {
|
|
66890
|
+
return versionMap[actionName];
|
|
66891
|
+
}
|
|
66892
|
+
};
|
|
66893
|
+
}
|
|
66894
|
+
const candidate_builder_DEFAULT_ACTION_VERSIONS = {
|
|
66895
|
+
"actions/setup-node": "v4",
|
|
66896
|
+
"actions/setup-python": "v5",
|
|
66897
|
+
"actions/setup-java": "v4",
|
|
66898
|
+
"actions/setup-ruby": "v1",
|
|
66899
|
+
"actions/setup-go": "v5",
|
|
66900
|
+
"jdx/mise-action": "v2"
|
|
66901
|
+
};
|
|
66902
|
+
const defaultActionVersionPort = createActionVersionPort(candidate_builder_DEFAULT_ACTION_VERSIONS);
|
|
66903
|
+
/**
|
|
66904
|
+
* Builds setup step candidates from detected environment.
|
|
66905
|
+
*/ async function buildCandidatesFromEnvironment(environment, versionGateway = defaultActionVersionPort, config = DEFAULT_COPILOT_SETUP_CONFIG) {
|
|
66906
|
+
const versionTypeToAction = getVersionTypeToActionMap(config);
|
|
66907
|
+
const versionFileConfigKeys = getVersionFileConfigKeyMap(config);
|
|
66740
66908
|
const candidates = [];
|
|
66741
|
-
// If mise.toml is present, add mise-action only
|
|
66742
66909
|
if (environment.hasMise) {
|
|
66743
66910
|
const miseVersion = await versionGateway.getVersion("jdx/mise-action");
|
|
66744
66911
|
candidates.push({
|
|
@@ -66748,24 +66915,14 @@ Example resolved format: actions/setup-node@1a2b3c4d5e6f # v4.0.0`;
|
|
|
66748
66915
|
});
|
|
66749
66916
|
return candidates;
|
|
66750
66917
|
}
|
|
66751
|
-
// Otherwise, add individual setup actions for each version file
|
|
66752
66918
|
const setupCandidates = await buildCandidatesFromVersionFiles(environment.versionFiles, versionTypeToAction, versionFileConfigKeys, versionGateway);
|
|
66753
66919
|
candidates.push(...setupCandidates);
|
|
66754
|
-
|
|
66755
|
-
const installCandidates = buildInstallCandidatesFromPackageManagers(environment.packageManagers, loadedConfig);
|
|
66920
|
+
const installCandidates = buildInstallCandidatesFromPackageManagers(environment.packageManagers, config);
|
|
66756
66921
|
candidates.push(...installCandidates);
|
|
66757
66922
|
return candidates;
|
|
66758
66923
|
}
|
|
66759
|
-
|
|
66760
|
-
* Builds setup step candidates from individual version files
|
|
66761
|
-
* @param versionFiles Array of version files to process
|
|
66762
|
-
* @param versionTypeToAction Map from version file type to action name
|
|
66763
|
-
* @param versionFileConfigKeys Map from version file type to config key
|
|
66764
|
-
* @param versionGateway Gateway for looking up action versions
|
|
66765
|
-
* @returns Array of setup step candidates
|
|
66766
|
-
*/ async function buildCandidatesFromVersionFiles(versionFiles, versionTypeToAction, versionFileConfigKeys, versionGateway) {
|
|
66924
|
+
async function buildCandidatesFromVersionFiles(versionFiles, versionTypeToAction, versionFileConfigKeys, versionGateway) {
|
|
66767
66925
|
const candidates = [];
|
|
66768
|
-
// Track which types we've already added to deduplicate (e.g., .nvmrc and .node-version)
|
|
66769
66926
|
const addedTypes = new Set();
|
|
66770
66927
|
for (const versionFile of versionFiles){
|
|
66771
66928
|
if (addedTypes.has(versionFile.type)) {
|
|
@@ -66789,30 +66946,19 @@ Example resolved format: actions/setup-node@1a2b3c4d5e6f # v4.0.0`;
|
|
|
66789
66946
|
}
|
|
66790
66947
|
return candidates;
|
|
66791
66948
|
}
|
|
66792
|
-
|
|
66793
|
-
* Builds install step candidates from detected package managers
|
|
66794
|
-
* @param packageManagers Array of detected package managers
|
|
66795
|
-
* @param config Configuration for package manager mappings
|
|
66796
|
-
* @returns Array of install step candidates
|
|
66797
|
-
*/ function buildInstallCandidatesFromPackageManagers(packageManagers, config) {
|
|
66949
|
+
function buildInstallCandidatesFromPackageManagers(packageManagers, config) {
|
|
66798
66950
|
const candidates = [];
|
|
66799
66951
|
const addedTypes = new Set();
|
|
66800
66952
|
for (const pm of packageManagers){
|
|
66801
|
-
// Skip if we've already added this package manager type
|
|
66802
66953
|
if (addedTypes.has(pm.type)) {
|
|
66803
66954
|
continue;
|
|
66804
66955
|
}
|
|
66805
66956
|
addedTypes.add(pm.type);
|
|
66806
|
-
// Find the config for this package manager
|
|
66807
66957
|
const pmConfig = config.packageManagers.find((c)=>c.type === pm.type);
|
|
66808
66958
|
if (!pmConfig) {
|
|
66809
66959
|
continue;
|
|
66810
66960
|
}
|
|
66811
|
-
// Determine a descriptive name for the install step
|
|
66812
66961
|
const stepName = getInstallStepName(pm.type);
|
|
66813
|
-
// Create install step candidate
|
|
66814
|
-
// Note: Empty action string indicates this is a run step (uses 'run' field instead of 'uses')
|
|
66815
|
-
// This is checked in workflow-generator.ts buildStepFromCandidate()
|
|
66816
66962
|
candidates.push({
|
|
66817
66963
|
action: "",
|
|
66818
66964
|
source: "version-file",
|
|
@@ -66822,9 +66968,7 @@ Example resolved format: actions/setup-node@1a2b3c4d5e6f # v4.0.0`;
|
|
|
66822
66968
|
}
|
|
66823
66969
|
return candidates;
|
|
66824
66970
|
}
|
|
66825
|
-
|
|
66826
|
-
* Gets a descriptive name for an install step based on package manager type
|
|
66827
|
-
*/ function getInstallStepName(packageManagerType) {
|
|
66971
|
+
function getInstallStepName(packageManagerType) {
|
|
66828
66972
|
const names = {
|
|
66829
66973
|
npm: "Install Node.js dependencies",
|
|
66830
66974
|
yarn: "Install Node.js dependencies",
|
|
@@ -67234,6 +67378,10 @@ function getGlobalWacContext() {
|
|
|
67234
67378
|
|
|
67235
67379
|
|
|
67236
67380
|
|
|
67381
|
+
const workflow_generator_DEFAULT_ACTION_VERSIONS = {
|
|
67382
|
+
"actions/checkout": "v4"
|
|
67383
|
+
};
|
|
67384
|
+
const workflow_generator_defaultActionVersionPort = createActionVersionPort(workflow_generator_DEFAULT_ACTION_VERSIONS);
|
|
67237
67385
|
/**
|
|
67238
67386
|
* Generates a human-readable step name from an action name
|
|
67239
67387
|
* @example "actions/setup-node" -> "Setup node"
|
|
@@ -67348,7 +67496,7 @@ function getGlobalWacContext() {
|
|
|
67348
67496
|
* @param versionGateway Optional gateway for looking up action versions (defaults to local)
|
|
67349
67497
|
* @param options Optional generation options for placeholders and resolved versions
|
|
67350
67498
|
* @returns The workflow YAML content as a string
|
|
67351
|
-
*/ async function generateWorkflowContent(candidates, versionGateway =
|
|
67499
|
+
*/ async function generateWorkflowContent(candidates, versionGateway = workflow_generator_defaultActionVersionPort, options) {
|
|
67352
67500
|
const stepOptions = {
|
|
67353
67501
|
usePlaceholders: options?.usePlaceholders,
|
|
67354
67502
|
resolvedVersions: options?.resolvedVersions
|
|
@@ -67406,7 +67554,7 @@ function getGlobalWacContext() {
|
|
|
67406
67554
|
* @param versionGateway Optional gateway for looking up action versions (defaults to local)
|
|
67407
67555
|
* @param options Optional generation options for placeholders and resolved versions
|
|
67408
67556
|
* @returns The updated workflow YAML content
|
|
67409
|
-
*/ async function updateWorkflowWithMissingSteps(existingWorkflow, missingCandidates, versionGateway =
|
|
67557
|
+
*/ async function updateWorkflowWithMissingSteps(existingWorkflow, missingCandidates, versionGateway = workflow_generator_defaultActionVersionPort, options) {
|
|
67410
67558
|
if (!existingWorkflow || typeof existingWorkflow !== "object") {
|
|
67411
67559
|
return generateWorkflowContent(missingCandidates, versionGateway, options);
|
|
67412
67560
|
}
|
|
@@ -67459,12 +67607,13 @@ function getGlobalWacContext() {
|
|
|
67459
67607
|
*/ async function gatherCandidates(dir, workflowsDirExists) {
|
|
67460
67608
|
const environmentGateway = createEnvironmentGateway();
|
|
67461
67609
|
const workflowGateway = createWorkflowGateway();
|
|
67610
|
+
const copilotSetupConfig = await loadCopilotSetupConfig();
|
|
67462
67611
|
// Detect environment configuration
|
|
67463
67612
|
const environment = await environmentGateway.detectEnvironment(dir);
|
|
67464
67613
|
// Parse existing workflows for setup actions
|
|
67465
67614
|
const workflowCandidates = workflowsDirExists ? await workflowGateway.parseWorkflowsForSetupActions(dir) : [];
|
|
67466
67615
|
// Build candidates from environment
|
|
67467
|
-
const envCandidates = await buildCandidatesFromEnvironment(environment);
|
|
67616
|
+
const envCandidates = await buildCandidatesFromEnvironment(environment, undefined, copilotSetupConfig);
|
|
67468
67617
|
// Merge candidates (workflow takes precedence)
|
|
67469
67618
|
return mergeCandidates(workflowCandidates, envCandidates);
|
|
67470
67619
|
}
|
|
@@ -67535,7 +67684,7 @@ function getGlobalWacContext() {
|
|
|
67535
67684
|
return types_errorResponse(`Target directory does not exist: ${dir}`);
|
|
67536
67685
|
}
|
|
67537
67686
|
const workflowGateway = createWorkflowGateway();
|
|
67538
|
-
const workflowsDir = (
|
|
67687
|
+
const workflowsDir = await file_system_utils_resolveSafePath(dir, ".github/workflows");
|
|
67539
67688
|
const workflowsDirExists = await file_system_utils_fileExists(workflowsDir);
|
|
67540
67689
|
// Gather all candidates
|
|
67541
67690
|
const allCandidates = await gatherCandidates(dir, workflowsDirExists);
|