dlw-machine-setup 0.6.2 → 0.7.1
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/bin/installer.js +212 -23
- package/package.json +1 -1
package/bin/installer.js
CHANGED
|
@@ -3850,7 +3850,7 @@ var import_fs6 = require("fs");
|
|
|
3850
3850
|
var import_path6 = require("path");
|
|
3851
3851
|
|
|
3852
3852
|
// src/bundles/types.ts
|
|
3853
|
-
var
|
|
3853
|
+
var SUPPORTED_SCHEMA_VERSIONS = [1, 2];
|
|
3854
3854
|
|
|
3855
3855
|
// src/utils/marker-block.ts
|
|
3856
3856
|
var import_fs5 = require("fs");
|
|
@@ -3881,11 +3881,106 @@ ${endMarker}`;
|
|
|
3881
3881
|
}
|
|
3882
3882
|
}
|
|
3883
3883
|
|
|
3884
|
+
// src/profiles/claude-code.ts
|
|
3885
|
+
var claudeCodeProfile = {
|
|
3886
|
+
agent: "claude-code",
|
|
3887
|
+
instructionsFile: "CLAUDE.md",
|
|
3888
|
+
handlers: {
|
|
3889
|
+
agent: {
|
|
3890
|
+
supported: true,
|
|
3891
|
+
destination: (name) => `.claude/agents/${name}.md`
|
|
3892
|
+
},
|
|
3893
|
+
skill: {
|
|
3894
|
+
supported: true,
|
|
3895
|
+
// Claude reads skills from a folder per skill: `.claude/skills/<name>/SKILL.md`.
|
|
3896
|
+
destination: (name) => `.claude/skills/${name}/SKILL.md`
|
|
3897
|
+
},
|
|
3898
|
+
hook: {
|
|
3899
|
+
supported: true,
|
|
3900
|
+
configFile: ".claude/settings.json",
|
|
3901
|
+
configKey: "hooks",
|
|
3902
|
+
scriptDir: ".claude/hooks"
|
|
3903
|
+
},
|
|
3904
|
+
"instructions-snippet": {
|
|
3905
|
+
supported: true
|
|
3906
|
+
}
|
|
3907
|
+
}
|
|
3908
|
+
};
|
|
3909
|
+
|
|
3910
|
+
// src/profiles/github-copilot.ts
|
|
3911
|
+
var githubCopilotProfile = {
|
|
3912
|
+
agent: "github-copilot",
|
|
3913
|
+
instructionsFile: ".github/copilot-instructions.md",
|
|
3914
|
+
handlers: {
|
|
3915
|
+
agent: {
|
|
3916
|
+
supported: true,
|
|
3917
|
+
// TODO(phase-3): verify destination path against Copilot CLI docs.
|
|
3918
|
+
destination: (name) => `.github/agents/${name}.md`
|
|
3919
|
+
// TODO(phase-3): supply frontmatter rewriter that maps Claude's
|
|
3920
|
+
// {name, description, disable-model-invocation, ...} to whatever
|
|
3921
|
+
// Copilot expects ({name, description, category, version, ...}).
|
|
3922
|
+
},
|
|
3923
|
+
skill: {
|
|
3924
|
+
supported: true,
|
|
3925
|
+
// TODO(phase-3): verify destination path against Copilot CLI docs.
|
|
3926
|
+
destination: (name) => `.github/skills/${name}.skill.md`
|
|
3927
|
+
// TODO(phase-3): supply frontmatter rewriter.
|
|
3928
|
+
},
|
|
3929
|
+
hook: {
|
|
3930
|
+
// TODO(phase-3): verify whether Copilot CLI has a hook primitive.
|
|
3931
|
+
// If yes, flip to supported: true and fill in configFile/configKey/scriptDir.
|
|
3932
|
+
// If no, leave as unsupported — assets of type 'hook' are silently skipped.
|
|
3933
|
+
supported: false
|
|
3934
|
+
},
|
|
3935
|
+
"instructions-snippet": {
|
|
3936
|
+
supported: true
|
|
3937
|
+
}
|
|
3938
|
+
}
|
|
3939
|
+
};
|
|
3940
|
+
|
|
3941
|
+
// src/profiles/cursor.ts
|
|
3942
|
+
var cursorProfile = {
|
|
3943
|
+
agent: "cursor",
|
|
3944
|
+
instructionsFile: ".cursor/rules/instructions.mdc",
|
|
3945
|
+
handlers: {
|
|
3946
|
+
agent: { supported: false },
|
|
3947
|
+
skill: { supported: false },
|
|
3948
|
+
hook: { supported: false },
|
|
3949
|
+
"instructions-snippet": { supported: true }
|
|
3950
|
+
}
|
|
3951
|
+
};
|
|
3952
|
+
|
|
3953
|
+
// src/profiles/index.ts
|
|
3954
|
+
var PROFILES = {
|
|
3955
|
+
"claude-code": claudeCodeProfile,
|
|
3956
|
+
"github-copilot": githubCopilotProfile,
|
|
3957
|
+
"cursor": cursorProfile
|
|
3958
|
+
};
|
|
3959
|
+
function getProfile(agent) {
|
|
3960
|
+
const profile = PROFILES[agent];
|
|
3961
|
+
if (!profile) {
|
|
3962
|
+
const known = Object.keys(PROFILES).map((k) => `"${k}"`).join(", ");
|
|
3963
|
+
throw new Error(
|
|
3964
|
+
`No profile registered for agent "${agent}". Known agents: ${known}. Add src/profiles/${agent}.ts and register it in src/profiles/index.ts.`
|
|
3965
|
+
);
|
|
3966
|
+
}
|
|
3967
|
+
return profile;
|
|
3968
|
+
}
|
|
3969
|
+
|
|
3884
3970
|
// src/bundles/run-bundle.ts
|
|
3885
3971
|
var OWNER_KEY = "_bundleOwner";
|
|
3886
3972
|
async function runBundle(manifest, ctx) {
|
|
3887
3973
|
assertSchemaCompatible(manifest);
|
|
3974
|
+
assertNameValid(manifest);
|
|
3888
3975
|
assertBundleRootExists(ctx);
|
|
3976
|
+
switch (manifest.schemaVersion) {
|
|
3977
|
+
case 1:
|
|
3978
|
+
return runBundleV1(manifest, ctx);
|
|
3979
|
+
case 2:
|
|
3980
|
+
return runBundleV2(manifest, ctx);
|
|
3981
|
+
}
|
|
3982
|
+
}
|
|
3983
|
+
function runBundleV1(manifest, ctx) {
|
|
3889
3984
|
const result = {
|
|
3890
3985
|
name: manifest.name,
|
|
3891
3986
|
opsExecuted: 0,
|
|
@@ -3896,30 +3991,120 @@ async function runBundle(manifest, ctx) {
|
|
|
3896
3991
|
executeOp(op, manifest.name, ctx, result);
|
|
3897
3992
|
result.opsExecuted++;
|
|
3898
3993
|
}
|
|
3899
|
-
|
|
3900
|
-
upsertMarkerBlock(
|
|
3901
|
-
(0, import_path6.join)(ctx.projectPath, ".gitignore"),
|
|
3902
|
-
`# ${manifest.name}:start`,
|
|
3903
|
-
`# ${manifest.name}:end`,
|
|
3904
|
-
manifest.gitignore.join("\n")
|
|
3905
|
-
);
|
|
3906
|
-
result.filesTouched.push(".gitignore");
|
|
3907
|
-
}
|
|
3994
|
+
writeGitignoreBlock(manifest, ctx, result);
|
|
3908
3995
|
const snippet = manifest.instructions?.[ctx.agent];
|
|
3909
3996
|
if (snippet) {
|
|
3910
3997
|
result.instructionsSnippet = snippet;
|
|
3911
|
-
|
|
3912
|
-
upsertMarkerBlock(
|
|
3913
|
-
(0, import_path6.join)(ctx.projectPath, ctx.instructionsFile),
|
|
3914
|
-
`<!-- ${manifest.name}:start -->`,
|
|
3915
|
-
`<!-- ${manifest.name}:end -->`,
|
|
3916
|
-
snippet
|
|
3917
|
-
);
|
|
3918
|
-
result.filesTouched.push(ctx.instructionsFile);
|
|
3919
|
-
}
|
|
3998
|
+
writeInstructionsBlock(manifest.name, snippet, ctx.instructionsFile, ctx, result);
|
|
3920
3999
|
}
|
|
3921
4000
|
return result;
|
|
3922
4001
|
}
|
|
4002
|
+
function runBundleV2(manifest, ctx) {
|
|
4003
|
+
const profile = getProfile(ctx.agent);
|
|
4004
|
+
const result = {
|
|
4005
|
+
name: manifest.name,
|
|
4006
|
+
opsExecuted: 0,
|
|
4007
|
+
filesTouched: []
|
|
4008
|
+
};
|
|
4009
|
+
const hookPatches = /* @__PURE__ */ new Map();
|
|
4010
|
+
for (const asset of manifest.assets ?? []) {
|
|
4011
|
+
runAsset(asset, profile, hookPatches, ctx, result);
|
|
4012
|
+
result.opsExecuted++;
|
|
4013
|
+
}
|
|
4014
|
+
for (const [file, patch] of hookPatches) {
|
|
4015
|
+
runMergeJson({ op: "merge-json", file, patch }, manifest.name, ctx, result);
|
|
4016
|
+
}
|
|
4017
|
+
for (const op of manifest.ops ?? []) {
|
|
4018
|
+
executeOp(op, manifest.name, ctx, result);
|
|
4019
|
+
result.opsExecuted++;
|
|
4020
|
+
}
|
|
4021
|
+
writeGitignoreBlock(manifest, ctx, result);
|
|
4022
|
+
const snippet = result.instructionsSnippet;
|
|
4023
|
+
if (snippet) {
|
|
4024
|
+
const instructionsFile = profile.instructionsFile || ctx.instructionsFile;
|
|
4025
|
+
writeInstructionsBlock(manifest.name, snippet, instructionsFile, ctx, result);
|
|
4026
|
+
}
|
|
4027
|
+
return result;
|
|
4028
|
+
}
|
|
4029
|
+
function runAsset(asset, profile, hookPatches, ctx, result) {
|
|
4030
|
+
switch (asset.type) {
|
|
4031
|
+
case "agent":
|
|
4032
|
+
runAssetCopy(asset, profile.handlers.agent, ctx, result);
|
|
4033
|
+
return;
|
|
4034
|
+
case "skill":
|
|
4035
|
+
runAssetCopy(asset, profile.handlers.skill, ctx, result);
|
|
4036
|
+
return;
|
|
4037
|
+
case "hook":
|
|
4038
|
+
accumulateHook(asset, profile.handlers.hook, hookPatches, ctx, result);
|
|
4039
|
+
return;
|
|
4040
|
+
case "instructions-snippet":
|
|
4041
|
+
if (profile.handlers["instructions-snippet"].supported) {
|
|
4042
|
+
result.instructionsSnippet = asset.content;
|
|
4043
|
+
}
|
|
4044
|
+
return;
|
|
4045
|
+
}
|
|
4046
|
+
}
|
|
4047
|
+
function runAssetCopy(asset, handler, ctx, result) {
|
|
4048
|
+
if (!handler.supported || !handler.destination) return;
|
|
4049
|
+
const source = resolveBundlePath(asset.source, ctx);
|
|
4050
|
+
if (!(0, import_fs6.existsSync)(source)) return;
|
|
4051
|
+
const target = resolveProjectPath(handler.destination(asset.name), ctx);
|
|
4052
|
+
if ((0, import_fs6.statSync)(source).isDirectory()) {
|
|
4053
|
+
copyDirectory(source, target);
|
|
4054
|
+
} else {
|
|
4055
|
+
(0, import_fs6.mkdirSync)((0, import_path6.dirname)(target), { recursive: true });
|
|
4056
|
+
(0, import_fs6.copyFileSync)(source, target);
|
|
4057
|
+
}
|
|
4058
|
+
result.filesTouched.push(handler.destination(asset.name));
|
|
4059
|
+
}
|
|
4060
|
+
function accumulateHook(asset, handler, hookPatches, ctx, result) {
|
|
4061
|
+
if (!handler.supported) return;
|
|
4062
|
+
if (!handler.configFile || !handler.configKey) return;
|
|
4063
|
+
if (asset.script && handler.scriptDir) {
|
|
4064
|
+
const scriptSource = resolveBundlePath(asset.script, ctx);
|
|
4065
|
+
if ((0, import_fs6.existsSync)(scriptSource)) {
|
|
4066
|
+
const scriptName = (0, import_path6.basename)(asset.script);
|
|
4067
|
+
const scriptRelDest = `${handler.scriptDir}/${scriptName}`;
|
|
4068
|
+
const scriptDest = resolveProjectPath(scriptRelDest, ctx);
|
|
4069
|
+
(0, import_fs6.mkdirSync)((0, import_path6.dirname)(scriptDest), { recursive: true });
|
|
4070
|
+
(0, import_fs6.copyFileSync)(scriptSource, scriptDest);
|
|
4071
|
+
result.filesTouched.push(scriptRelDest);
|
|
4072
|
+
}
|
|
4073
|
+
}
|
|
4074
|
+
const command = handler.scriptDir ? asset.command.replace(/\{hookDir\}/g, handler.scriptDir) : asset.command;
|
|
4075
|
+
const entry = {
|
|
4076
|
+
hooks: [{ type: "command", command }]
|
|
4077
|
+
};
|
|
4078
|
+
if (asset.matcher) entry.matcher = asset.matcher;
|
|
4079
|
+
let patch = hookPatches.get(handler.configFile);
|
|
4080
|
+
if (!patch) {
|
|
4081
|
+
patch = {};
|
|
4082
|
+
hookPatches.set(handler.configFile, patch);
|
|
4083
|
+
}
|
|
4084
|
+
const root = patch[handler.configKey] ??= {};
|
|
4085
|
+
const events = root[asset.event] ??= [];
|
|
4086
|
+
events.push(entry);
|
|
4087
|
+
}
|
|
4088
|
+
function writeGitignoreBlock(manifest, ctx, result) {
|
|
4089
|
+
if (!manifest.gitignore?.length) return;
|
|
4090
|
+
upsertMarkerBlock(
|
|
4091
|
+
(0, import_path6.join)(ctx.projectPath, ".gitignore"),
|
|
4092
|
+
`# ${manifest.name}:start`,
|
|
4093
|
+
`# ${manifest.name}:end`,
|
|
4094
|
+
manifest.gitignore.join("\n")
|
|
4095
|
+
);
|
|
4096
|
+
result.filesTouched.push(".gitignore");
|
|
4097
|
+
}
|
|
4098
|
+
function writeInstructionsBlock(bundleName, snippet, instructionsFile, ctx, result) {
|
|
4099
|
+
if (ctx.skipInstructions) return;
|
|
4100
|
+
upsertMarkerBlock(
|
|
4101
|
+
(0, import_path6.join)(ctx.projectPath, instructionsFile),
|
|
4102
|
+
`<!-- ${bundleName}:start -->`,
|
|
4103
|
+
`<!-- ${bundleName}:end -->`,
|
|
4104
|
+
snippet
|
|
4105
|
+
);
|
|
4106
|
+
result.filesTouched.push(instructionsFile);
|
|
4107
|
+
}
|
|
3923
4108
|
function executeOp(op, owner, ctx, result) {
|
|
3924
4109
|
switch (op.op) {
|
|
3925
4110
|
case "copy":
|
|
@@ -4021,11 +4206,14 @@ function resolveProjectPath(rel, ctx) {
|
|
|
4021
4206
|
return full;
|
|
4022
4207
|
}
|
|
4023
4208
|
function assertSchemaCompatible(manifest) {
|
|
4024
|
-
if (manifest.schemaVersion
|
|
4209
|
+
if (!SUPPORTED_SCHEMA_VERSIONS.includes(manifest.schemaVersion)) {
|
|
4210
|
+
const supported = SUPPORTED_SCHEMA_VERSIONS.join(", ");
|
|
4025
4211
|
throw new Error(
|
|
4026
|
-
`Bundle "${manifest.name}" declares schemaVersion ${manifest.schemaVersion}, installer supports ${
|
|
4212
|
+
`Bundle "${manifest.name}" declares schemaVersion ${manifest.schemaVersion}, installer supports ${supported}. Upgrade the installer or re-pin the bundle.`
|
|
4027
4213
|
);
|
|
4028
4214
|
}
|
|
4215
|
+
}
|
|
4216
|
+
function assertNameValid(manifest) {
|
|
4029
4217
|
if (!manifest.name || !/^[a-z][a-z0-9-]*$/.test(manifest.name)) {
|
|
4030
4218
|
throw new Error(
|
|
4031
4219
|
`Bundle name must match /^[a-z][a-z0-9-]*$/: got "${manifest.name}"`
|
|
@@ -4219,8 +4407,8 @@ function extractFirstHeading(filePath) {
|
|
|
4219
4407
|
}
|
|
4220
4408
|
} catch {
|
|
4221
4409
|
}
|
|
4222
|
-
const
|
|
4223
|
-
return
|
|
4410
|
+
const basename2 = filePath.split(/[/\\]/).pop() ?? "";
|
|
4411
|
+
return basename2.replace(/\.md$/i, "").replace(/[-_]+/g, " ");
|
|
4224
4412
|
}
|
|
4225
4413
|
function formatPathRef(contextPath, description, agent) {
|
|
4226
4414
|
if (agent === "github-copilot") {
|
|
@@ -4471,6 +4659,7 @@ var package_default = {
|
|
|
4471
4659
|
scripts: {
|
|
4472
4660
|
dev: "tsx src/index.ts",
|
|
4473
4661
|
build: 'esbuild src/index.ts --bundle --platform=node --banner:js="#!/usr/bin/env node" --outfile=wrapper/bin/installer.js',
|
|
4662
|
+
"build:all": "npm run build",
|
|
4474
4663
|
"publish:wrapper": "cd wrapper && npm publish"
|
|
4475
4664
|
},
|
|
4476
4665
|
dependencies: {
|