dlw-machine-setup 0.5.13 → 0.5.15

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.
Files changed (2) hide show
  1. package/bin/installer.js +120 -101
  2. package/package.json +1 -1
package/bin/installer.js CHANGED
@@ -3309,7 +3309,8 @@ async function fetchWizardOptions(token, repo) {
3309
3309
  });
3310
3310
  if (!assetRes.ok) return null;
3311
3311
  const data = await assetRes.json();
3312
- if (!Array.isArray(data.agents) || !Array.isArray(data.personas?.personas)) {
3312
+ const techArray = data.personas?.technologies ?? data.personas?.personas;
3313
+ if (!Array.isArray(data.agents) || !Array.isArray(techArray)) {
3313
3314
  return null;
3314
3315
  }
3315
3316
  return { options: data, releaseVersion: release.tag_name ?? "unknown" };
@@ -3571,9 +3572,9 @@ async function loadWizardOptions(token, repo) {
3571
3572
  Object.entries(options.mcpServers).filter(([, config]) => config.active !== false)
3572
3573
  );
3573
3574
  return {
3574
- personas: options.personas.personas.filter((p) => p.active !== false),
3575
+ technologies: (options.personas?.technologies ?? options.personas?.personas ?? []).filter((p) => p.active !== false),
3575
3576
  agents: options.agents.filter((a) => a.active !== false),
3576
- baseMcpServers: options.personas.baseMcpServers,
3577
+ baseMcpServers: options.personas?.baseMcpServers ?? [],
3577
3578
  mcpServers: filteredMcpServers,
3578
3579
  releaseVersion
3579
3580
  };
@@ -3635,8 +3636,9 @@ var MIN_FILE_SIZE = 1024;
3635
3636
  var fetch_contexts_default = defineStep({
3636
3637
  name: "fetch-contexts",
3637
3638
  label: "Downloading contexts",
3639
+ when: (ctx) => ctx.config.technologies.length > 0,
3638
3640
  execute: async (ctx) => {
3639
- const uniqueDomains = [...new Set(ctx.config.personas.flatMap((p) => p.domains))];
3641
+ const uniqueDomains = [...new Set(ctx.config.technologies.flatMap((p) => p.domains))];
3640
3642
  const domainValues = uniqueDomains.map((d) => d.toLowerCase());
3641
3643
  const downloadResult = await fetchContexts(domainValues, ctx.token, ctx.repo, ctx.config.projectPath);
3642
3644
  ctx.installed.domainsInstalled = downloadResult.successful;
@@ -3780,7 +3782,14 @@ var import_fs3 = require("fs");
3780
3782
  var import_path3 = require("path");
3781
3783
  var import_child_process2 = require("child_process");
3782
3784
  var MIN_FILE_SIZE2 = 1024;
3783
- var FACTORY_ASSET_NAME = "factory.tar.gz";
3785
+ function getFactoryAsset(agent) {
3786
+ switch (agent) {
3787
+ case "github-copilot":
3788
+ return { assetName: "factory-copilot.tar.gz", rootFolder: "factory-copilot" };
3789
+ default:
3790
+ return { assetName: "factory.tar.gz", rootFolder: "factory" };
3791
+ }
3792
+ }
3784
3793
  var fetch_factory_default = defineStep({
3785
3794
  name: "fetch-factory",
3786
3795
  label: "Installing Factory framework",
@@ -3794,37 +3803,6 @@ var fetch_factory_default = defineStep({
3794
3803
  return { status: "success", message: result.filesInstalled.join(", ") };
3795
3804
  }
3796
3805
  });
3797
- function getAgentPaths(agent, targetDir) {
3798
- const claudeDir = (0, import_path3.join)(targetDir, ".claude");
3799
- const githubDir = (0, import_path3.join)(targetDir, ".github");
3800
- switch (agent) {
3801
- case "github-copilot":
3802
- return {
3803
- agentsDir: (0, import_path3.join)(githubDir, "agents"),
3804
- hooksDir: (0, import_path3.join)(claudeDir, "hooks"),
3805
- // Copilot reads .claude/hooks/ natively
3806
- skillsDir: (0, import_path3.join)(githubDir, "prompts"),
3807
- // Copilot uses .github/prompts/ for slash commands
3808
- settingsDir: claudeDir,
3809
- // Copilot reads .claude/settings.json natively
3810
- agentsLabel: ".github/agents/",
3811
- hooksLabel: ".claude/hooks/",
3812
- skillsLabel: ".github/prompts/",
3813
- settingsLabel: ".claude/settings.json"
3814
- };
3815
- default:
3816
- return {
3817
- agentsDir: (0, import_path3.join)(claudeDir, "agents"),
3818
- hooksDir: (0, import_path3.join)(claudeDir, "hooks"),
3819
- skillsDir: (0, import_path3.join)(claudeDir, "skills"),
3820
- settingsDir: claudeDir,
3821
- agentsLabel: ".claude/agents/",
3822
- hooksLabel: ".claude/hooks/",
3823
- skillsLabel: ".claude/skills/",
3824
- settingsLabel: ".claude/settings.json"
3825
- };
3826
- }
3827
- }
3828
3806
  async function fetchFactory(token, repo, targetDir, agent) {
3829
3807
  const result = { success: false, filesInstalled: [] };
3830
3808
  const tarCheck = (0, import_child_process2.spawnSync)("tar", ["--version"], { stdio: "ignore" });
@@ -3832,6 +3810,7 @@ async function fetchFactory(token, repo, targetDir, agent) {
3832
3810
  result.failureReason = "tar command not found";
3833
3811
  return result;
3834
3812
  }
3813
+ const { assetName, rootFolder } = getFactoryAsset(agent);
3835
3814
  const headers = {
3836
3815
  "Accept": "application/vnd.github+json",
3837
3816
  "Authorization": `Bearer ${token}`
@@ -3845,9 +3824,9 @@ async function fetchFactory(token, repo, targetDir, agent) {
3845
3824
  return result;
3846
3825
  }
3847
3826
  const releaseData = await releaseResponse.json();
3848
- const asset = releaseData.assets?.find((a) => a.name === FACTORY_ASSET_NAME);
3827
+ const asset = releaseData.assets?.find((a) => a.name === assetName);
3849
3828
  if (!asset) {
3850
- result.failureReason = `${FACTORY_ASSET_NAME} not found in release`;
3829
+ result.failureReason = `${assetName} not found in release`;
3851
3830
  return result;
3852
3831
  }
3853
3832
  const tempDir = (0, import_path3.join)(targetDir, ".temp-factory-download");
@@ -3861,7 +3840,7 @@ async function fetchFactory(token, repo, targetDir, agent) {
3861
3840
  result.failureReason = `Download failed (${response.status})`;
3862
3841
  return result;
3863
3842
  }
3864
- const archivePath = (0, import_path3.join)(tempDir, FACTORY_ASSET_NAME);
3843
+ const archivePath = (0, import_path3.join)(tempDir, assetName);
3865
3844
  const arrayBuffer = await response.arrayBuffer();
3866
3845
  (0, import_fs3.writeFileSync)(archivePath, Buffer.from(arrayBuffer));
3867
3846
  const stats = (0, import_fs3.statSync)(archivePath);
@@ -3888,54 +3867,17 @@ async function fetchFactory(token, repo, targetDir, agent) {
3888
3867
  return result;
3889
3868
  }
3890
3869
  const extractedEntries = (0, import_fs3.readdirSync)(tempDir);
3891
- const extractedFolder = extractedEntries.find((e) => e.toLowerCase() === "factory");
3870
+ const extractedFolder = extractedEntries.find((e) => e.toLowerCase() === rootFolder.toLowerCase());
3892
3871
  if (!extractedFolder) {
3893
- result.failureReason = "No factory/ folder in archive";
3872
+ result.failureReason = `No ${rootFolder}/ folder in archive`;
3894
3873
  return result;
3895
3874
  }
3896
3875
  const extractedPath = (0, import_path3.join)(tempDir, extractedFolder);
3897
- const paths = getAgentPaths(agent, targetDir);
3898
- const factoryDir = (0, import_path3.join)(targetDir, "factory");
3899
- const agentsSrc = (0, import_path3.join)(extractedPath, "agents");
3900
- if ((0, import_fs3.existsSync)(agentsSrc)) {
3901
- copyDirectory2(agentsSrc, paths.agentsDir);
3902
- result.filesInstalled.push(paths.agentsLabel);
3903
- }
3904
- const hooksSrc = (0, import_path3.join)(extractedPath, "hooks");
3905
- if ((0, import_fs3.existsSync)(hooksSrc)) {
3906
- copyDirectory2(hooksSrc, paths.hooksDir);
3907
- result.filesInstalled.push(paths.hooksLabel);
3908
- }
3909
- const skillsSrc = (0, import_path3.join)(extractedPath, "skills");
3910
- if ((0, import_fs3.existsSync)(skillsSrc)) {
3911
- copyDirectory2(skillsSrc, paths.skillsDir);
3912
- result.filesInstalled.push(paths.skillsLabel);
3913
- }
3914
- const workflowSrc = (0, import_path3.join)(extractedPath, "workflow");
3915
- if ((0, import_fs3.existsSync)(workflowSrc)) {
3916
- copyDirectory2(workflowSrc, (0, import_path3.join)(factoryDir, "workflow"));
3917
- result.filesInstalled.push("factory/workflow/");
3918
- }
3919
- const utilsSrc = (0, import_path3.join)(extractedPath, "utils");
3920
- if ((0, import_fs3.existsSync)(utilsSrc)) {
3921
- copyDirectory2(utilsSrc, (0, import_path3.join)(factoryDir, "utils"));
3922
- result.filesInstalled.push("factory/utils/");
3923
- }
3924
- const docsSrc = (0, import_path3.join)(extractedPath, "docs");
3925
- if ((0, import_fs3.existsSync)(docsSrc)) {
3926
- copyDirectory2(docsSrc, (0, import_path3.join)(factoryDir, "docs"));
3927
- result.filesInstalled.push("factory/docs/");
3928
- }
3929
- const rootEntries = (0, import_fs3.readdirSync)(extractedPath, { withFileTypes: true });
3930
- for (const entry of rootEntries) {
3931
- if (entry.isFile()) {
3932
- if (!(0, import_fs3.existsSync)(factoryDir)) (0, import_fs3.mkdirSync)(factoryDir, { recursive: true });
3933
- (0, import_fs3.copyFileSync)((0, import_path3.join)(extractedPath, entry.name), (0, import_path3.join)(factoryDir, entry.name));
3934
- result.filesInstalled.push(`factory/${entry.name}`);
3935
- }
3876
+ if (agent === "github-copilot") {
3877
+ installCopilotFactory(extractedPath, targetDir, result);
3878
+ } else {
3879
+ installClaudeFactory(extractedPath, targetDir, result);
3936
3880
  }
3937
- configureSettings(paths.settingsDir);
3938
- result.filesInstalled.push(paths.settingsLabel);
3939
3881
  result.success = true;
3940
3882
  } catch (error) {
3941
3883
  result.failureReason = error instanceof Error ? error.message : String(error);
@@ -3949,6 +3891,62 @@ async function fetchFactory(token, repo, targetDir, agent) {
3949
3891
  }
3950
3892
  return result;
3951
3893
  }
3894
+ function installClaudeFactory(extractedPath, targetDir, result) {
3895
+ const claudeDir = (0, import_path3.join)(targetDir, ".claude");
3896
+ const factoryDir = (0, import_path3.join)(targetDir, "factory");
3897
+ const agentsSrc = (0, import_path3.join)(extractedPath, "agents");
3898
+ if ((0, import_fs3.existsSync)(agentsSrc)) {
3899
+ copyDirectory2(agentsSrc, (0, import_path3.join)(claudeDir, "agents"));
3900
+ result.filesInstalled.push(".claude/agents/");
3901
+ }
3902
+ const hooksSrc = (0, import_path3.join)(extractedPath, "hooks");
3903
+ if ((0, import_fs3.existsSync)(hooksSrc)) {
3904
+ copyDirectory2(hooksSrc, (0, import_path3.join)(claudeDir, "hooks"));
3905
+ result.filesInstalled.push(".claude/hooks/");
3906
+ }
3907
+ const skillsSrc = (0, import_path3.join)(extractedPath, "skills");
3908
+ if ((0, import_fs3.existsSync)(skillsSrc)) {
3909
+ copyDirectory2(skillsSrc, (0, import_path3.join)(claudeDir, "skills"));
3910
+ result.filesInstalled.push(".claude/skills/");
3911
+ }
3912
+ const workflowSrc = (0, import_path3.join)(extractedPath, "workflow");
3913
+ if ((0, import_fs3.existsSync)(workflowSrc)) {
3914
+ copyDirectory2(workflowSrc, (0, import_path3.join)(factoryDir, "workflow"));
3915
+ result.filesInstalled.push("factory/workflow/");
3916
+ }
3917
+ const utilsSrc = (0, import_path3.join)(extractedPath, "utils");
3918
+ if ((0, import_fs3.existsSync)(utilsSrc)) {
3919
+ copyDirectory2(utilsSrc, (0, import_path3.join)(factoryDir, "utils"));
3920
+ result.filesInstalled.push("factory/utils/");
3921
+ }
3922
+ const docsSrc = (0, import_path3.join)(extractedPath, "docs");
3923
+ if ((0, import_fs3.existsSync)(docsSrc)) {
3924
+ copyDirectory2(docsSrc, (0, import_path3.join)(factoryDir, "docs"));
3925
+ result.filesInstalled.push("factory/docs/");
3926
+ }
3927
+ const rootEntries = (0, import_fs3.readdirSync)(extractedPath, { withFileTypes: true });
3928
+ for (const entry of rootEntries) {
3929
+ if (entry.isFile()) {
3930
+ if (!(0, import_fs3.existsSync)(factoryDir)) (0, import_fs3.mkdirSync)(factoryDir, { recursive: true });
3931
+ (0, import_fs3.copyFileSync)((0, import_path3.join)(extractedPath, entry.name), (0, import_path3.join)(factoryDir, entry.name));
3932
+ result.filesInstalled.push(`factory/${entry.name}`);
3933
+ }
3934
+ }
3935
+ configureSettings(claudeDir);
3936
+ result.filesInstalled.push(".claude/settings.json");
3937
+ }
3938
+ function installCopilotFactory(extractedPath, targetDir, result) {
3939
+ const factoryDir = (0, import_path3.join)(targetDir, "factory");
3940
+ copyDirectory2(extractedPath, factoryDir);
3941
+ const entries = (0, import_fs3.readdirSync)(extractedPath, { withFileTypes: true });
3942
+ for (const entry of entries) {
3943
+ if (entry.isDirectory()) {
3944
+ result.filesInstalled.push(`factory/${entry.name}/`);
3945
+ } else {
3946
+ result.filesInstalled.push(`factory/${entry.name}`);
3947
+ }
3948
+ }
3949
+ }
3952
3950
  function configureSettings(claudeDir) {
3953
3951
  const settingsPath = (0, import_path3.join)(claudeDir, "settings.json");
3954
3952
  let settings = {};
@@ -4029,12 +4027,12 @@ var import_path4 = require("path");
4029
4027
  // src/steps/shared.ts
4030
4028
  function getFilteredMcpConfig(ctx) {
4031
4029
  const successfulDomains = new Set(ctx.installed.domainsInstalled ?? []);
4032
- const successfulPersonas = ctx.config.personas.filter(
4030
+ const successfulTechnologies = ctx.config.technologies.filter(
4033
4031
  (p) => p.domains.every((d) => successfulDomains.has(d.toLowerCase()))
4034
4032
  );
4035
4033
  const successfulMcpServers = /* @__PURE__ */ new Set([
4036
4034
  ...ctx.config.baseMcpServers,
4037
- ...successfulPersonas.flatMap((p) => p.mcpServers)
4035
+ ...successfulTechnologies.flatMap((p) => p.mcpServers)
4038
4036
  ]);
4039
4037
  return Object.fromEntries(
4040
4038
  Object.entries(ctx.config.mcpConfig).filter(([name]) => successfulMcpServers.has(name))
@@ -4048,7 +4046,7 @@ var red = (text) => `\x1B[31m${text}\x1B[0m`;
4048
4046
  var write_instructions_default = defineStep({
4049
4047
  name: "write-instructions",
4050
4048
  label: "Writing instruction file",
4051
- when: (ctx) => (ctx.installed.domainsInstalled?.length ?? 0) > 0,
4049
+ when: (ctx) => (ctx.installed.domainsInstalled?.length ?? 0) > 0 || Object.keys(ctx.config.mcpConfig).length > 0 || ctx.installed.factoryInstalled,
4052
4050
  execute: async (ctx) => {
4053
4051
  const filteredMcpConfig = getFilteredMcpConfig(ctx);
4054
4052
  const domains = ctx.installed.domainsInstalled;
@@ -4245,7 +4243,25 @@ function buildMCPSection(mcpConfig) {
4245
4243
  }
4246
4244
  return lines2.join("\n");
4247
4245
  }
4248
- function buildFactorySection() {
4246
+ function buildFactorySection(agent) {
4247
+ if (agent === "github-copilot") {
4248
+ return [
4249
+ `## Factory Dev Workflow`,
4250
+ ``,
4251
+ `Factory is installed. Skills are in \`factory/skills/\`, templates in \`factory/templates/\`.`,
4252
+ ``,
4253
+ `Read \`factory/README.md\` and \`factory/PROTOCOL.md\` for usage instructions.`,
4254
+ ``,
4255
+ `Available skills:`,
4256
+ `- \`factory-advisor\` \u2014 start or resume the workflow (main entry point)`,
4257
+ `- \`factory-init\` \u2014 initialize project state`,
4258
+ `- \`factory-planner\` \u2014 create story plan`,
4259
+ `- \`factory-executor\` \u2014 execute development tasks`,
4260
+ `- \`factory-verifier\` \u2014 code review`,
4261
+ `- \`factory-completer\` \u2014 finalize and complete work`,
4262
+ ``
4263
+ ].join("\n");
4264
+ }
4249
4265
  return [
4250
4266
  `## Factory Dev Workflow`,
4251
4267
  ``,
@@ -4277,7 +4293,7 @@ function buildCombinedInstructions(domains, mcpConfig, agent, projectPath, facto
4277
4293
  const lines2 = [`# AI Development Instructions`, ``, `> Generated by One-Shot Installer`, ``];
4278
4294
  lines2.push(buildContextRefsSection(domains, agent, contextsDir));
4279
4295
  if (Object.keys(mcpConfig).length > 0) lines2.push(buildMCPSection(mcpConfig));
4280
- if (factoryInstalled) lines2.push(buildFactorySection());
4296
+ if (factoryInstalled) lines2.push(buildFactorySection(agent));
4281
4297
  return lines2.join("\n");
4282
4298
  }
4283
4299
 
@@ -4288,7 +4304,7 @@ var red2 = (text) => `\x1B[31m${text}\x1B[0m`;
4288
4304
  var write_mcp_config_default = defineStep({
4289
4305
  name: "write-mcp-config",
4290
4306
  label: "Writing MCP configuration",
4291
- when: (ctx) => (ctx.installed.domainsInstalled?.length ?? 0) > 0,
4307
+ when: (ctx) => Object.keys(ctx.config.mcpConfig).length > 0,
4292
4308
  execute: async (ctx) => {
4293
4309
  const filteredMcpConfig = getFilteredMcpConfig(ctx);
4294
4310
  const target = getAgentMCPTarget(ctx.config.agent);
@@ -4406,7 +4422,7 @@ var update_gitignore_default = defineStep({
4406
4422
  var import_fs7 = require("fs");
4407
4423
  var import_path7 = require("path");
4408
4424
  var import_os2 = require("os");
4409
- var INSTALLER_VERSION = "0.5.13";
4425
+ var INSTALLER_VERSION = "0.5.15";
4410
4426
  var write_state_default = defineStep({
4411
4427
  name: "write-state",
4412
4428
  label: "Saving installation state",
@@ -4422,7 +4438,7 @@ var write_state_default = defineStep({
4422
4438
  releaseVersion: ctx.config.releaseVersion,
4423
4439
  projectPath: ctx.config.projectPath,
4424
4440
  agent: ctx.config.agent,
4425
- personas: ctx.config.personas.map((p) => p.id),
4441
+ technologies: ctx.config.technologies.map((p) => p.id),
4426
4442
  domains: ctx.installed.domainsInstalled ?? [],
4427
4443
  mcpServers: mcpServersAdded,
4428
4444
  mcpConfigs: filteredMcpConfig,
@@ -4565,20 +4581,23 @@ async function main() {
4565
4581
  }
4566
4582
  async function collectInputs(options, releaseVersion, factoryAvailable = false) {
4567
4583
  const selectedIds = await esm_default2({
4568
- message: "Personas:",
4584
+ message: "Technology:",
4569
4585
  instructions: " Space to select \xB7 Enter to confirm",
4570
- choices: options.personas.map((p) => ({
4571
- name: p.name.replace(/ Developer$/, ""),
4572
- value: p.id
4573
- })),
4586
+ choices: [
4587
+ ...options.technologies.map((p) => ({
4588
+ name: p.name.replace(/ Developer$/, ""),
4589
+ value: p.id
4590
+ })),
4591
+ { name: "None", value: "none" }
4592
+ ],
4574
4593
  required: true
4575
4594
  });
4576
- const selectedPersonas = options.personas.filter((p) => selectedIds.includes(p.id));
4595
+ const selectedTechnologies = options.technologies.filter((p) => selectedIds.includes(p.id));
4577
4596
  const agent = await esm_default5({
4578
4597
  message: "AI coding tool:",
4579
4598
  choices: options.agents
4580
4599
  });
4581
- const mcpConfig = buildMCPConfiguration(selectedPersonas, options.baseMcpServers, options.mcpServers);
4600
+ const mcpConfig = buildMCPConfiguration(selectedTechnologies, options.baseMcpServers, options.mcpServers);
4582
4601
  let azureDevOpsOrg = "";
4583
4602
  if (mcpConfig["azure-devops"]) {
4584
4603
  const org = await esm_default4({
@@ -4598,7 +4617,7 @@ async function collectInputs(options, releaseVersion, factoryAvailable = false)
4598
4617
  default: (0, import_path8.resolve)(process.cwd())
4599
4618
  });
4600
4619
  return {
4601
- personas: selectedPersonas,
4620
+ technologies: selectedTechnologies,
4602
4621
  agent,
4603
4622
  azureDevOpsOrg,
4604
4623
  projectPath: (0, import_path8.resolve)(projectInput),
@@ -4609,7 +4628,7 @@ async function collectInputs(options, releaseVersion, factoryAvailable = false)
4609
4628
  };
4610
4629
  }
4611
4630
  async function previewAndConfirm(config, options) {
4612
- const personaNames = config.personas.map((p) => p.name.replace(/ Developer$/, "")).join(", ");
4631
+ const technologyNames = config.technologies.length > 0 ? config.technologies.map((p) => p.name.replace(/ Developer$/, "")).join(", ") : "None";
4613
4632
  const agentDisplay = options.agents.find((a) => a.value === config.agent)?.name ?? config.agent;
4614
4633
  const instructionFile = getInstructionFilePath3(config.agent);
4615
4634
  const mcpConfigFile = getMCPConfigPath2(config.agent);
@@ -4620,7 +4639,7 @@ async function previewAndConfirm(config, options) {
4620
4639
  const instructionMode = detectMarkerFileMode(instructionFilePath, "<!-- one-shot-installer:start -->");
4621
4640
  const mcpMode = detectMCPFileMode(mcpConfigFilePath);
4622
4641
  const gitignoreMode = detectMarkerFileMode(gitignorePath, "# one-shot-installer:start");
4623
- const uniqueDomains = [...new Set(config.personas.flatMap((p) => p.domains))];
4642
+ const uniqueDomains = [...new Set(config.technologies.flatMap((p) => p.domains))];
4624
4643
  const domainModes = uniqueDomains.map((d) => ({
4625
4644
  domain: d,
4626
4645
  mode: detectContextMode(config.projectPath, d)
@@ -4628,7 +4647,7 @@ async function previewAndConfirm(config, options) {
4628
4647
  console.log("\n" + "\u2500".repeat(48));
4629
4648
  console.log(" Ready to install");
4630
4649
  console.log("\u2500".repeat(48) + "\n");
4631
- console.log(` Personas ${personaNames}`);
4650
+ console.log(` Technology ${technologyNames}`);
4632
4651
  console.log(` Tool ${agentDisplay}`);
4633
4652
  console.log(` Factory ${config.installFactory ? "yes" : "no"}`);
4634
4653
  console.log(` Directory ${config.projectPath}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dlw-machine-setup",
3
- "version": "0.5.13",
3
+ "version": "0.5.15",
4
4
  "description": "One-shot installer for The Machine toolchain",
5
5
  "bin": {
6
6
  "dlw-machine-setup": "bin/installer.js"