create-harper 1.4.5 → 1.4.7

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/index.js CHANGED
@@ -1,81 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import * as prompts from '@clack/prompts';
3
- import path from 'node:path';
4
- import { helpMessage } from './lib/constants/helpMessage.js';
5
- import { pkgFromUserAgent } from './lib/pkg/pkgFromUserAgent.js';
6
- import { checkForUpdate } from './lib/steps/checkForUpdate.js';
7
- import { getEnvVars } from './lib/steps/getEnvVars.js';
8
- import { getPackageName } from './lib/steps/getPackageName.js';
9
- import { getProjectName } from './lib/steps/getProjectName.js';
10
- import { getRunAppImmediately } from './lib/steps/getRunAppImmediately.js';
11
- import { getTemplate } from './lib/steps/getTemplate.js';
12
- import { handleExistingDir } from './lib/steps/handleExistingDir.js';
13
- import { helpAgents } from './lib/steps/helpAgents.js';
14
- import { installAndOptionallyStart } from './lib/steps/installAndOptionallyStart.js';
15
- import { parseArgv } from './lib/steps/parseArgv.js';
16
- import { scaffoldProject } from './lib/steps/scaffoldProject.js';
2
+ import { init } from './lib/init.js';
17
3
 
18
- init().catch((e) => {
19
- console.error(e);
20
- });
21
-
22
- async function init() {
23
- const args = parseArgv(process.argv.slice(2));
24
-
25
- if (args.help) {
26
- console.log(helpMessage);
27
- return;
28
- }
29
-
30
- const currentVersion = await checkForUpdate();
31
- if (args.version) {
32
- console.log(`Current version: ${currentVersion}`);
33
- return;
34
- }
35
-
36
- const interactive = args.interactive;
37
-
38
- // Detect AI agent environment for better agent experience (AX)
39
- await helpAgents(interactive);
40
-
41
- const cancel = () => prompts.cancel('Operation cancelled');
42
-
43
- // Get the project name and target directory
44
- const projectNameResult = await getProjectName(args.targetDir, interactive);
45
- if (projectNameResult.cancelled) { return cancel(); }
46
- const { projectName, targetDir } = projectNameResult;
47
-
48
- // Handle if the directory exists and isn't empty
49
- const handleExistingDirResult = await handleExistingDir(targetDir, args.overwrite, interactive);
50
- if (handleExistingDirResult.cancelled) { return cancel(); }
51
-
52
- // Get the package name
53
- const packageNameResult = await getPackageName(targetDir, interactive);
54
- if (packageNameResult.cancelled) { return cancel(); }
55
- const { packageName } = packageNameResult;
56
-
57
- // Choose a framework and variant
58
- const templateResult = await getTemplate(args.template, interactive);
59
- if (templateResult.cancelled) { return cancel(); }
60
- const { template } = templateResult;
61
-
62
- // Get environment variables for .env file
63
- const envVarsResult = await getEnvVars(interactive, template, args.deploymentUsername, args.deploymentURL);
64
- if (envVarsResult.cancelled) { return cancel(); }
65
- const { envVars } = envVarsResult;
66
-
67
- // Should we do a package manager installation?
68
- const pkgInfo = pkgFromUserAgent(process.env.npm_config_user_agent);
69
- const pkgManager = pkgInfo ? pkgInfo.name : 'npm';
70
- const immediateResult = await getRunAppImmediately(args.immediate, interactive, pkgManager);
71
- if (immediateResult.cancelled) { return cancel(); }
72
- const { immediate } = immediateResult;
73
-
74
- // Write out the contents based on all prior steps.
75
- const cwd = process.cwd();
76
- const root = path.join(cwd, targetDir);
77
- scaffoldProject(root, projectName, packageName, template, envVars);
78
-
79
- // Log out the next steps.
80
- installAndOptionallyStart(root, pkgManager, immediate, args.skipInstall);
81
- }
4
+ init();
package/lib/init.js ADDED
@@ -0,0 +1,86 @@
1
+ import * as prompts from '@clack/prompts';
2
+ import path from 'node:path';
3
+ import { helpMessage } from './constants/helpMessage.js';
4
+ import { pkgFromUserAgent } from './pkg/pkgFromUserAgent.js';
5
+ import { checkForUpdate } from './steps/checkForUpdate.js';
6
+ import { getEnvVars } from './steps/getEnvVars.js';
7
+ import { getPackageName } from './steps/getPackageName.js';
8
+ import { getProjectName } from './steps/getProjectName.js';
9
+ import { getRunAppImmediately } from './steps/getRunAppImmediately.js';
10
+ import { getSkills } from './steps/getSkills.js';
11
+ import { getTemplate } from './steps/getTemplate.js';
12
+ import { handleExistingDir } from './steps/handleExistingDir.js';
13
+ import { helpAgents } from './steps/helpAgents.js';
14
+ import { installAndOptionallyStart } from './steps/installAndOptionallyStart.js';
15
+ import { parseArgv } from './steps/parseArgv.js';
16
+ import { scaffoldProject } from './steps/scaffoldProject.js';
17
+
18
+ export async function init() {
19
+ try {
20
+ const args = parseArgv(process.argv.slice(2));
21
+
22
+ if (args.help) {
23
+ console.log(helpMessage);
24
+ return;
25
+ }
26
+
27
+ const currentVersion = await checkForUpdate();
28
+ if (args.version) {
29
+ console.log(`Current version: ${currentVersion}`);
30
+ return;
31
+ }
32
+
33
+ const interactive = args.interactive;
34
+
35
+ // Detect AI agent environment for better agent experience (AX)
36
+ await helpAgents(interactive);
37
+
38
+ const cancel = () => prompts.cancel('Operation cancelled');
39
+
40
+ // Get the project name and target directory
41
+ const projectNameResult = await getProjectName(args.targetDir, interactive);
42
+ if (projectNameResult.cancelled) { return cancel(); }
43
+ const { projectName, targetDir } = projectNameResult;
44
+
45
+ // Handle if the directory exists and isn't empty
46
+ const handleExistingDirResult = await handleExistingDir(targetDir, args.overwrite, interactive);
47
+ if (handleExistingDirResult.cancelled) { return cancel(); }
48
+
49
+ // Get the package name
50
+ const packageNameResult = await getPackageName(targetDir, interactive);
51
+ if (packageNameResult.cancelled) { return cancel(); }
52
+ const { packageName } = packageNameResult;
53
+
54
+ // Choose a framework and variant
55
+ const templateResult = await getTemplate(args.template, interactive);
56
+ if (templateResult.cancelled) { return cancel(); }
57
+ const { template } = templateResult;
58
+
59
+ // Get environment variables for .env file
60
+ const envVarsResult = await getEnvVars(interactive, template, args.deploymentUsername, args.deploymentURL);
61
+ if (envVarsResult.cancelled) { return cancel(); }
62
+ const { envVars } = envVarsResult;
63
+
64
+ // Should we do a package manager installation?
65
+ const pkgInfo = pkgFromUserAgent(process.env.npm_config_user_agent);
66
+ const pkgManager = pkgInfo ? pkgInfo.name : 'npm';
67
+ const immediateResult = await getRunAppImmediately(args.immediate, interactive, pkgManager);
68
+ if (immediateResult.cancelled) { return cancel(); }
69
+ const { immediate } = immediateResult;
70
+
71
+ // Choose skills to install
72
+ const skillsResult = await getSkills(interactive, args.skills, args.agents, args.skipSkills);
73
+ if (skillsResult.cancelled) { return cancel(); }
74
+ const { selectedSkills, selectedAgents } = skillsResult;
75
+
76
+ // Write out the contents based on all prior steps.
77
+ const cwd = process.cwd();
78
+ const root = path.join(cwd, targetDir);
79
+ scaffoldProject(root, projectName, packageName, template, envVars);
80
+
81
+ // Log out the next steps.
82
+ installAndOptionallyStart(root, pkgManager, immediate, args.skipInstall, selectedSkills, selectedAgents);
83
+ } catch (e) {
84
+ console.error(e);
85
+ }
86
+ }
package/lib/install.js CHANGED
@@ -7,22 +7,43 @@ import { run } from './run.js';
7
7
  *
8
8
  * @param {string} root - The root directory of the project.
9
9
  * @param {string} agent - The package manager agent (e.g., 'npm', 'pnpm', 'yarn', 'bun').
10
+ * @param {string[]} [selectedSkills] - The skills to install.
11
+ * @param {string[]} [selectedAgents] - The agents to install skills to.
10
12
  */
11
- export function install(root, agent) {
13
+ export function install(root, agent, selectedSkills = [], selectedAgents = []) {
12
14
  if (process.env._HARPER_TEST_CLI) {
13
15
  prompts.log.step(
14
16
  `Installing dependencies with ${agent}... (skipped in test)`,
15
17
  );
16
18
  return;
17
19
  }
20
+
21
+ if (selectedSkills.length > 0) {
22
+ prompts.log.step('Installing Harper skills...');
23
+
24
+ const command = ['npx', '-y', 'skills', 'add', 'harperfast/skills'];
25
+
26
+ if (selectedSkills.includes('*') && selectedAgents.includes('*')) {
27
+ command.push('--all');
28
+ } else {
29
+ if (selectedSkills.length > 0) {
30
+ command.push('--skill', ...selectedSkills);
31
+ }
32
+ if (selectedAgents.length > 0) {
33
+ command.push('--agent', ...selectedAgents);
34
+ }
35
+ command.push('--yes');
36
+ }
37
+
38
+ run(command, {
39
+ stdio: 'inherit',
40
+ cwd: root,
41
+ });
42
+ }
43
+
18
44
  prompts.log.step(`Installing dependencies with ${agent}...`);
19
45
  run(getInstallCommand(agent), {
20
46
  stdio: 'inherit',
21
47
  cwd: root,
22
48
  });
23
- prompts.log.step('Installing Harper skills...');
24
- run(['npx', '-y', 'skills', 'add', 'harperfast/skills', '--all', '--yes'], {
25
- stdio: 'inherit',
26
- cwd: root,
27
- });
28
49
  }
@@ -0,0 +1,61 @@
1
+ import * as prompts from '@clack/prompts';
2
+
3
+ /**
4
+ * Step 6: Choose whether to install Harper AI skills and for which agents.
5
+ *
6
+ * @param {boolean} interactive - Whether the CLI is running in interactive mode.
7
+ * @param {string} [argSkills] - The skills specified in the command line args.
8
+ * @param {string} [argAgents] - The agents specified in the command line args.
9
+ * @param {boolean} [argSkipSkills] - Whether to skip skills installation.
10
+ * @returns {Promise<{selectedSkills: string[], selectedAgents: string[], cancelled: boolean}>}
11
+ */
12
+ export async function getSkills(interactive, argSkills, argAgents, argSkipSkills) {
13
+ if (argSkipSkills) {
14
+ return { selectedSkills: [], selectedAgents: [], cancelled: false };
15
+ }
16
+
17
+ let selectedSkills = argSkills ? argSkills.split(',') : ['*'];
18
+ let selectedAgents = argAgents ? argAgents.split(',') : [];
19
+
20
+ if (interactive && !argSkills && !argAgents) {
21
+ const installSkills = await prompts.confirm({
22
+ message: 'Would you like to install Harper AI skills to your IDE/agent?',
23
+ initialValue: true,
24
+ });
25
+
26
+ if (prompts.isCancel(installSkills)) {
27
+ return { selectedSkills: [], selectedAgents: [], cancelled: true };
28
+ }
29
+
30
+ if (!installSkills) {
31
+ return { selectedSkills: [], selectedAgents: [], cancelled: false };
32
+ }
33
+
34
+ const agents = await prompts.multiselect({
35
+ message: 'Which AI agents do you use? (Press <space> to select, <enter> to submit)',
36
+ options: [
37
+ { value: 'auto', label: 'Just detect', hint: 'Auto-detect current environment' },
38
+ { value: 'claude-code', label: 'Claude Code', hint: 'https://claude.com/product/claude-code' },
39
+ { value: 'codex', label: 'OpenAI Codex', hint: 'https://chatgpt.com/codex' },
40
+ { value: 'copilot', label: 'GitHub Copilot', hint: 'https://github.com/features/copilot' },
41
+ { value: 'cursor', label: 'Cursor', hint: 'https://cursor.com/' },
42
+ { value: 'windsurf', label: 'Windsurf', hint: 'https://windsurf.com/' },
43
+ { value: 'all', label: 'All supported agents', hint: '40+ agents' },
44
+ ],
45
+ required: false,
46
+ });
47
+
48
+ if (prompts.isCancel(agents)) {
49
+ return { selectedSkills: [], selectedAgents: [], cancelled: true };
50
+ }
51
+
52
+ selectedAgents = agents;
53
+ if (agents.includes('all')) {
54
+ selectedAgents = ['*'];
55
+ } else if (agents.includes('auto')) {
56
+ selectedAgents = [];
57
+ }
58
+ }
59
+
60
+ return { selectedSkills, selectedAgents, cancelled: false };
61
+ }
@@ -12,10 +12,12 @@ import { start } from '../start.js';
12
12
  * @param {string} pkgManager - The detected or selected package manager.
13
13
  * @param {boolean} immediate - Whether to immediately install and start the project.
14
14
  * @param {boolean} skipInstall - Whether to skip the installation step.
15
+ * @param {string[]} selectedSkills - The skills to install.
16
+ * @param {string[]} selectedAgents - The agents to install skills to.
15
17
  */
16
- export function installAndOptionallyStart(root, pkgManager, immediate, skipInstall) {
18
+ export function installAndOptionallyStart(root, pkgManager, immediate, skipInstall, selectedSkills, selectedAgents) {
17
19
  if (!skipInstall) {
18
- install(root, pkgManager);
20
+ install(root, pkgManager, selectedSkills, selectedAgents);
19
21
  }
20
22
 
21
23
  if (immediate && !skipInstall) {
@@ -9,14 +9,18 @@ export function parseArgv(rawArgv) {
9
9
  'interactive',
10
10
  'overwrite',
11
11
  'skipInstall',
12
+ 'skipSkills',
12
13
  'version',
13
14
  ],
14
15
  string: [
16
+ 'agents',
15
17
  'deploymentURL',
16
18
  'deploymentUsername',
19
+ 'skills',
17
20
  'template',
18
21
  ],
19
22
  alias: {
23
+ a: 'agents',
20
24
  c: 'deploymentURL',
21
25
  'deployment-url': 'deploymentURL',
22
26
  'deployment-username': 'deploymentUsername',
@@ -25,6 +29,7 @@ export function parseArgv(rawArgv) {
25
29
  o: 'overwrite',
26
30
  s: 'skipInstall',
27
31
  'skip-install': 'skipInstall',
32
+ 'skip-skills': 'skipSkills',
28
33
  t: 'template',
29
34
  u: 'deploymentUsername',
30
35
  v: 'version',
@@ -49,6 +54,7 @@ export function parseArgv(rawArgv) {
49
54
  }
50
55
 
51
56
  return {
57
+ agents: argv.agents,
52
58
  deploymentURL: argv.deploymentURL,
53
59
  deploymentUsername: argv.deploymentUsername,
54
60
  help: argv.help,
@@ -56,6 +62,8 @@ export function parseArgv(rawArgv) {
56
62
  interactive: argv.interactive ?? process.stdin.isTTY,
57
63
  overwrite: argv.overwrite,
58
64
  skipInstall: argv.skipInstall,
65
+ skipSkills: argv.skipSkills,
66
+ skills: argv.skills,
59
67
  targetDir: positionalArgs[0] ? formatTargetDir(String(positionalArgs[0])) : undefined,
60
68
  template: argv.template,
61
69
  version: argv.version,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "create-harper",
3
3
  "description": "Scaffold a new Harper project in JavaScript or TypeScript.",
4
- "version": "1.4.5",
4
+ "version": "1.4.7",
5
5
  "type": "module",
6
6
  "author": {
7
7
  "name": "HarperDB",