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 +2 -79
- package/lib/init.js +86 -0
- package/lib/install.js +27 -6
- package/lib/steps/getSkills.js +61 -0
- package/lib/steps/installAndOptionallyStart.js +4 -2
- package/lib/steps/parseArgv.js +8 -0
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -1,81 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
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()
|
|
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) {
|
package/lib/steps/parseArgv.js
CHANGED
|
@@ -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,
|