aiwcli 0.9.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/README.md +1248 -0
- package/bin/dev.cmd +3 -0
- package/bin/dev.js +16 -0
- package/bin/run.cmd +3 -0
- package/bin/run.js +19 -0
- package/dist/commands/branch.d.ts +45 -0
- package/dist/commands/branch.js +488 -0
- package/dist/commands/clean.d.ts +34 -0
- package/dist/commands/clean.js +186 -0
- package/dist/commands/clear.d.ts +51 -0
- package/dist/commands/clear.js +835 -0
- package/dist/commands/init/index.d.ts +107 -0
- package/dist/commands/init/index.js +565 -0
- package/dist/commands/launch.d.ts +21 -0
- package/dist/commands/launch.js +108 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/lib/base-command.d.ts +114 -0
- package/dist/lib/base-command.js +153 -0
- package/dist/lib/bmad-installer.d.ts +38 -0
- package/dist/lib/bmad-installer.js +145 -0
- package/dist/lib/claude-settings-types.d.ts +102 -0
- package/dist/lib/claude-settings-types.js +5 -0
- package/dist/lib/config.d.ts +25 -0
- package/dist/lib/config.js +46 -0
- package/dist/lib/debug.d.ts +39 -0
- package/dist/lib/debug.js +74 -0
- package/dist/lib/env-compat.d.ts +26 -0
- package/dist/lib/env-compat.js +35 -0
- package/dist/lib/errors.d.ts +126 -0
- package/dist/lib/errors.js +145 -0
- package/dist/lib/generic-merge.d.ts +74 -0
- package/dist/lib/generic-merge.js +105 -0
- package/dist/lib/git/branch.d.ts +67 -0
- package/dist/lib/git/branch.js +155 -0
- package/dist/lib/git/index.d.ts +11 -0
- package/dist/lib/git/index.js +13 -0
- package/dist/lib/git/safety-checks.d.ts +44 -0
- package/dist/lib/git/safety-checks.js +102 -0
- package/dist/lib/git/types.d.ts +31 -0
- package/dist/lib/git/types.js +6 -0
- package/dist/lib/git/worktree.d.ts +67 -0
- package/dist/lib/git/worktree.js +220 -0
- package/dist/lib/gitignore-manager.d.ts +10 -0
- package/dist/lib/gitignore-manager.js +60 -0
- package/dist/lib/hooks-merger.d.ts +28 -0
- package/dist/lib/hooks-merger.js +94 -0
- package/dist/lib/ide-path-resolver.d.ts +102 -0
- package/dist/lib/ide-path-resolver.js +129 -0
- package/dist/lib/index.d.ts +13 -0
- package/dist/lib/index.js +22 -0
- package/dist/lib/output.d.ts +51 -0
- package/dist/lib/output.js +76 -0
- package/dist/lib/paths.d.ts +66 -0
- package/dist/lib/paths.js +136 -0
- package/dist/lib/quiet.d.ts +12 -0
- package/dist/lib/quiet.js +17 -0
- package/dist/lib/settings-hierarchy.d.ts +42 -0
- package/dist/lib/settings-hierarchy.js +105 -0
- package/dist/lib/spawn.d.ts +105 -0
- package/dist/lib/spawn.js +157 -0
- package/dist/lib/spinner.d.ts +19 -0
- package/dist/lib/spinner.js +34 -0
- package/dist/lib/stdin.d.ts +48 -0
- package/dist/lib/stdin.js +60 -0
- package/dist/lib/template-installer.d.ts +92 -0
- package/dist/lib/template-installer.js +375 -0
- package/dist/lib/template-linter.d.ts +49 -0
- package/dist/lib/template-linter.js +173 -0
- package/dist/lib/template-merger.d.ts +47 -0
- package/dist/lib/template-merger.js +173 -0
- package/dist/lib/template-resolver.d.ts +20 -0
- package/dist/lib/template-resolver.js +60 -0
- package/dist/lib/terminal.d.ts +102 -0
- package/dist/lib/terminal.js +245 -0
- package/dist/lib/tty-detection.d.ts +62 -0
- package/dist/lib/tty-detection.js +83 -0
- package/dist/lib/user-utils.d.ts +5 -0
- package/dist/lib/user-utils.js +23 -0
- package/dist/lib/version.d.ts +99 -0
- package/dist/lib/version.js +144 -0
- package/dist/lib/watch-templates.d.ts +6 -0
- package/dist/lib/watch-templates.js +73 -0
- package/dist/lib/windsurf-hooks-hierarchy.d.ts +30 -0
- package/dist/lib/windsurf-hooks-hierarchy.js +66 -0
- package/dist/lib/windsurf-hooks-merger.d.ts +26 -0
- package/dist/lib/windsurf-hooks-merger.js +53 -0
- package/dist/lib/windsurf-hooks-types.d.ts +33 -0
- package/dist/lib/windsurf-hooks-types.js +5 -0
- package/dist/templates/CLAUDE.md +174 -0
- package/dist/templates/_shared/.claude/commands/handoff.md +14 -0
- package/dist/templates/_shared/.claude/settings.json +61 -0
- package/dist/templates/_shared/.codex/workflows/handoff.md +14 -0
- package/dist/templates/_shared/.windsurf/workflows/handoff.md +14 -0
- package/dist/templates/_shared/hooks/__init__.py +16 -0
- package/dist/templates/_shared/hooks/archive_plan.py +270 -0
- package/dist/templates/_shared/hooks/context_enforcer.py +621 -0
- package/dist/templates/_shared/hooks/context_monitor.py +322 -0
- package/dist/templates/_shared/hooks/file-suggestion.py +188 -0
- package/dist/templates/_shared/hooks/task_create_capture.py +194 -0
- package/dist/templates/_shared/hooks/task_update_capture.py +254 -0
- package/dist/templates/_shared/hooks/user_prompt_submit.py +157 -0
- package/dist/templates/_shared/lib/__init__.py +1 -0
- package/dist/templates/_shared/lib/base/__init__.py +49 -0
- package/dist/templates/_shared/lib/base/__pycache__/constants.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/atomic_write.py +180 -0
- package/dist/templates/_shared/lib/base/constants.py +299 -0
- package/dist/templates/_shared/lib/base/inference.py +189 -0
- package/dist/templates/_shared/lib/base/utils.py +216 -0
- package/dist/templates/_shared/lib/context/__init__.py +119 -0
- package/dist/templates/_shared/lib/context/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/cache.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/context_manager.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/event_log.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/cache.py +446 -0
- package/dist/templates/_shared/lib/context/context_manager.py +1171 -0
- package/dist/templates/_shared/lib/context/discovery.py +486 -0
- package/dist/templates/_shared/lib/context/event_log.py +308 -0
- package/dist/templates/_shared/lib/context/plan_archive.py +247 -0
- package/dist/templates/_shared/lib/context/task_sync.py +367 -0
- package/dist/templates/_shared/lib/handoff/__init__.py +22 -0
- package/dist/templates/_shared/lib/handoff/document_generator.py +307 -0
- package/dist/templates/_shared/lib/templates/README.md +215 -0
- package/dist/templates/_shared/lib/templates/__init__.py +40 -0
- package/dist/templates/_shared/lib/templates/formatters.py +147 -0
- package/dist/templates/_shared/lib/templates/plan_context.py +119 -0
- package/dist/templates/_shared/scripts/save_handoff.py +99 -0
- package/dist/templates/_shared/workflows/handoff.md +212 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/ACCESSIBILITY-TESTER.md +80 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/ARCHITECT-REVIEWER.md +75 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/ASSUMPTION-CHAIN-TRACER.md +239 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/CLARITY-AUDITOR.md +109 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/CODE-REVIEWER.md +71 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/COMPLETENESS-CHECKER.md +104 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/CONTEXT-EXTRACTOR.md +93 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/DEVILS-ADVOCATE.md +223 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/DOCUMENTATION-REVIEWER.md +73 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/FEASIBILITY-ANALYST.md +93 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/FRESH-PERSPECTIVE.md +103 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/HANDOFF-READINESS.md +145 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/HIDDEN-COMPLEXITY-DETECTOR.md +248 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/INCENTIVE-MAPPER.md +235 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/PENETRATION-TESTER.md +80 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/PERFORMANCE-ENGINEER.md +76 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/PLAN-ORCHESTRATOR.md +141 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/PRECEDENT-FINDER.md +240 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/REVERSIBILITY-ANALYST.md +211 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/RISK-ASSESSOR.md +101 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/SECOND-ORDER-ANALYST.md +197 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/SIMPLICITY-GUARDIAN.md +97 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/SKEPTIC.md +349 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/STAKEHOLDER-ADVOCATE.md +106 -0
- package/dist/templates/cc-native/.claude/agents/cc-native/TRADE-OFF-ILLUMINATOR.md +205 -0
- package/dist/templates/cc-native/.claude/commands/cc-native/fresh-perspective.md +8 -0
- package/dist/templates/cc-native/.claude/commands/cc-native/specdev.md +10 -0
- package/dist/templates/cc-native/.claude/settings.json +119 -0
- package/dist/templates/cc-native/.windsurf/workflows/cc-native/fix.md +8 -0
- package/dist/templates/cc-native/.windsurf/workflows/cc-native/fresh-perspective.md +8 -0
- package/dist/templates/cc-native/.windsurf/workflows/cc-native/implement.md +8 -0
- package/dist/templates/cc-native/.windsurf/workflows/cc-native/research.md +8 -0
- package/dist/templates/cc-native/CC-NATIVE-README.md +192 -0
- package/dist/templates/cc-native/MIGRATION.md +86 -0
- package/dist/templates/cc-native/TEMPLATE-SCHEMA.md +331 -0
- package/dist/templates/cc-native/_cc-native/docs/PERMISSION_REQUEST_VERIFICATION.md +147 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/add_plan_context.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/archive_plan.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/cc-native-agent-review.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/cc-native-plan-review.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/test_permission_request.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/add_plan_context.py +150 -0
- package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.py +746 -0
- package/dist/templates/cc-native/_cc-native/hooks/suggest-fresh-perspective.py +339 -0
- package/dist/templates/cc-native/_cc-native/lib/__init__.py +57 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/orchestrator.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/state.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/utils.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/async_archive.py +68 -0
- package/dist/templates/cc-native/_cc-native/lib/atomic_write.py +98 -0
- package/dist/templates/cc-native/_cc-native/lib/constants.py +45 -0
- package/dist/templates/cc-native/_cc-native/lib/orchestrator.py +273 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__init__.py +28 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/agent.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/base.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/codex.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__pycache__/gemini.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/agent.py +164 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/base.py +89 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/codex.py +119 -0
- package/dist/templates/cc-native/_cc-native/lib/reviewers/gemini.py +103 -0
- package/dist/templates/cc-native/_cc-native/lib/state.py +251 -0
- package/dist/templates/cc-native/_cc-native/lib/utils.py +830 -0
- package/dist/templates/cc-native/_cc-native/plan-review.config.json +76 -0
- package/dist/templates/cc-native/_cc-native/scripts/__pycache__/aggregate_agents.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/scripts/aggregate_agents.py +151 -0
- package/dist/templates/cc-native/_cc-native/workflows/fresh-perspective.md +134 -0
- package/dist/templates/cc-native/_cc-native/workflows/specdev.md +9 -0
- package/dist/types/exit-codes.d.ts +11 -0
- package/dist/types/exit-codes.js +10 -0
- package/dist/types/index.d.ts +5 -0
- package/dist/types/index.js +7 -0
- package/oclif.manifest.json +405 -0
- package/package.json +109 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Process spawning utilities for AI Workflow CLI.
|
|
3
|
+
*
|
|
4
|
+
* This module provides utilities for spawning external processes (e.g., Claude Code)
|
|
5
|
+
* with proper error handling, stdio configuration, and debug logging.
|
|
6
|
+
*
|
|
7
|
+
* ## Key Features
|
|
8
|
+
* - Spawn external processes with configurable stdio (inherit, pipe)
|
|
9
|
+
* - Exit code capture and error handling
|
|
10
|
+
* - Detached mode for parallel sessions
|
|
11
|
+
* - Debug logging integration
|
|
12
|
+
* - Cross-platform compatibility
|
|
13
|
+
*
|
|
14
|
+
* ## Usage Examples
|
|
15
|
+
*
|
|
16
|
+
* ### Basic Spawn (Interactive)
|
|
17
|
+
* ```typescript
|
|
18
|
+
* import {spawnProcess} from '../lib/spawn.js'
|
|
19
|
+
*
|
|
20
|
+
* const exitCode = await spawnProcess('claude', ['--dangerously-skip-permissions'])
|
|
21
|
+
* if (exitCode !== 0) {
|
|
22
|
+
* console.error('Claude Code exited with error')
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* ### Parallel Sessions (Detached Mode)
|
|
27
|
+
* ```typescript
|
|
28
|
+
* // Launch multiple Claude Code sessions concurrently
|
|
29
|
+
* const session1 = spawnProcess('claude', ['--dangerously-skip-permissions'], {detached: true})
|
|
30
|
+
* const session2 = spawnProcess('claude', ['--dangerously-skip-permissions'], {detached: true})
|
|
31
|
+
* await Promise.all([session1, session2])
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* ### Custom Working Directory
|
|
35
|
+
* ```typescript
|
|
36
|
+
* await spawnProcess('npm', ['install'], {cwd: '/path/to/project'})
|
|
37
|
+
* ```
|
|
38
|
+
*
|
|
39
|
+
* @module lib/spawn
|
|
40
|
+
*/
|
|
41
|
+
import { execSync, spawn as nodeSpawn } from 'node:child_process';
|
|
42
|
+
import { debug, debugSpawn } from './debug.js';
|
|
43
|
+
import { ProcessSpawnError } from './errors.js';
|
|
44
|
+
/**
|
|
45
|
+
* Spawn an external process and return its exit code.
|
|
46
|
+
*
|
|
47
|
+
* This function wraps Node.js child_process.spawn with AIW-specific
|
|
48
|
+
* error handling, debug logging, and parallel session support.
|
|
49
|
+
*
|
|
50
|
+
* ## Exit Code Mapping
|
|
51
|
+
* - 0: Success
|
|
52
|
+
* - Non-zero: Error (actual code from child process)
|
|
53
|
+
* - null code: Defaults to 1
|
|
54
|
+
*
|
|
55
|
+
* ## Error Handling
|
|
56
|
+
* - ENOENT: Command not found → ProcessSpawnError with install instructions
|
|
57
|
+
* - EACCES: Permission denied → ProcessSpawnError with permission fix
|
|
58
|
+
* - Other errors: ProcessSpawnError with error details
|
|
59
|
+
*
|
|
60
|
+
* ## Parallel Sessions
|
|
61
|
+
* Use `detached: true` to spawn independent processes that don't block parent.
|
|
62
|
+
* Multiple calls can run concurrently without conflicts.
|
|
63
|
+
*
|
|
64
|
+
* @param command - Command to execute (e.g., 'claude', 'npm', 'git')
|
|
65
|
+
* @param args - Arguments array (e.g., ['--dangerously-skip-permissions'])
|
|
66
|
+
* @param options - Spawn configuration options
|
|
67
|
+
* @returns Promise<number> - Exit code (0 = success, non-zero = error)
|
|
68
|
+
* @throws ProcessSpawnError - When spawn fails (command not found, permissions, etc.)
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* // Launch Claude Code with sandbox disabled
|
|
72
|
+
* const exitCode = await spawnProcess('claude', ['--dangerously-skip-permissions'])
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* // Parallel session with detached mode
|
|
76
|
+
* const session1 = spawnProcess('claude', ['--dangerously-skip-permissions'], {detached: true})
|
|
77
|
+
* const session2 = spawnProcess('claude', ['--dangerously-skip-permissions'], {detached: true})
|
|
78
|
+
* await Promise.all([session1, session2])
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* // Custom working directory
|
|
82
|
+
* await spawnProcess('npm', ['test'], {cwd: '/path/to/project'})
|
|
83
|
+
*/
|
|
84
|
+
export async function spawnProcess(command, args = [], options = {}) {
|
|
85
|
+
const { cwd, stdio = 'inherit', detached = false } = options;
|
|
86
|
+
// Log spawn details in debug mode
|
|
87
|
+
debugSpawn(command, args);
|
|
88
|
+
// Windows hybrid approach: try without shell first, fallback to .cmd if ENOENT
|
|
89
|
+
// This preserves error detection while supporting .cmd files from npm
|
|
90
|
+
if (process.platform === 'win32') {
|
|
91
|
+
try {
|
|
92
|
+
return await attemptSpawn(command, args, { cwd, stdio, detached, shell: false });
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
// If command not found and .cmd file exists, use cmd.exe wrapper
|
|
96
|
+
// This avoids DEP0190 deprecation warning while supporting npm-installed commands
|
|
97
|
+
if (error instanceof ProcessSpawnError && error.code === 'ENOENT' && commandExistsInPath(`${command}.cmd`)) {
|
|
98
|
+
// Use cmd.exe /c to execute .cmd file without shell mode or deprecation warning
|
|
99
|
+
return attemptSpawn('cmd.exe', ['/c', command, ...args], { cwd, stdio, detached, shell: false });
|
|
100
|
+
}
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// Unix: always use shell: false for security
|
|
105
|
+
return attemptSpawn(command, args, { cwd, stdio, detached, shell: false });
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Check if a command exists in PATH (Windows only).
|
|
109
|
+
* Uses 'where' command to check if file exists in PATH.
|
|
110
|
+
*/
|
|
111
|
+
function commandExistsInPath(command) {
|
|
112
|
+
try {
|
|
113
|
+
execSync(`where ${command}`, { stdio: 'ignore', windowsHide: true });
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Internal helper to attempt process spawn with given options.
|
|
122
|
+
*/
|
|
123
|
+
function attemptSpawn(command, args, spawnOptions) {
|
|
124
|
+
return new Promise((resolve, reject) => {
|
|
125
|
+
try {
|
|
126
|
+
const childProcess = nodeSpawn(command, args, spawnOptions);
|
|
127
|
+
// Handle spawn errors (ENOENT, EACCES, etc.)
|
|
128
|
+
childProcess.on('error', (error) => {
|
|
129
|
+
if (error.code === 'ENOENT') {
|
|
130
|
+
reject(new ProcessSpawnError(`Command not found: ${command}. Install Claude Code from https://claude.ai/download.`, 'ENOENT'));
|
|
131
|
+
}
|
|
132
|
+
else if (error.code === 'EACCES') {
|
|
133
|
+
reject(new ProcessSpawnError(`Permission denied: ${command}. Check file permissions.`, 'EACCES'));
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
reject(new ProcessSpawnError(`Failed to spawn ${command}: ${error.message}. Check that the command exists and is executable.`, error.code));
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
// Capture exit code on process close
|
|
140
|
+
childProcess.on('close', (code, signal) => {
|
|
141
|
+
const exitCode = code ?? 1; // Default to error if no code
|
|
142
|
+
if (signal) {
|
|
143
|
+
debug(`Process terminated by signal: ${signal}`);
|
|
144
|
+
}
|
|
145
|
+
debug(`Process exited with code: ${exitCode}`);
|
|
146
|
+
resolve(exitCode);
|
|
147
|
+
});
|
|
148
|
+
// Unref detached processes to allow parent to exit
|
|
149
|
+
if (spawnOptions.detached && childProcess.unref) {
|
|
150
|
+
childProcess.unref();
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
reject(new ProcessSpawnError(`Spawn error: ${error instanceof Error ? error.message : String(error)}`));
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spinner Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides TTY-aware spinner wrapper using ora library.
|
|
5
|
+
* Spinners automatically disable in non-TTY contexts (piped, redirected, CI).
|
|
6
|
+
*/
|
|
7
|
+
import { type Ora } from 'ora';
|
|
8
|
+
/**
|
|
9
|
+
* Create a TTY-aware spinner.
|
|
10
|
+
* Automatically disables in non-TTY contexts (piped, redirected, CI, quiet mode).
|
|
11
|
+
*/
|
|
12
|
+
export declare function createSpinner(text: string, flags?: {
|
|
13
|
+
quiet?: boolean;
|
|
14
|
+
}): Ora;
|
|
15
|
+
/**
|
|
16
|
+
* Helper for common "loading" operations with spinner.
|
|
17
|
+
* Automatically handles success/failure and cleanup.
|
|
18
|
+
*/
|
|
19
|
+
export declare function withSpinner<T>(text: string, operation: () => Promise<T>): Promise<T>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spinner Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides TTY-aware spinner wrapper using ora library.
|
|
5
|
+
* Spinners automatically disable in non-TTY contexts (piped, redirected, CI).
|
|
6
|
+
*/
|
|
7
|
+
import ora from 'ora';
|
|
8
|
+
import { shouldShowSpinners } from './tty-detection.js';
|
|
9
|
+
/**
|
|
10
|
+
* Create a TTY-aware spinner.
|
|
11
|
+
* Automatically disables in non-TTY contexts (piped, redirected, CI, quiet mode).
|
|
12
|
+
*/
|
|
13
|
+
export function createSpinner(text, flags) {
|
|
14
|
+
return ora({
|
|
15
|
+
isEnabled: shouldShowSpinners(flags),
|
|
16
|
+
text,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Helper for common "loading" operations with spinner.
|
|
21
|
+
* Automatically handles success/failure and cleanup.
|
|
22
|
+
*/
|
|
23
|
+
export async function withSpinner(text, operation) {
|
|
24
|
+
const spinner = createSpinner(text).start();
|
|
25
|
+
try {
|
|
26
|
+
const result = await operation();
|
|
27
|
+
spinner.succeed();
|
|
28
|
+
return result;
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
spinner.fail();
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* stdin Detection and Reading Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides utilities for detecting and reading piped input from stdin.
|
|
5
|
+
* Used by commands that need to accept piped data from other commands.
|
|
6
|
+
*
|
|
7
|
+
* @example Basic stdin detection
|
|
8
|
+
* ```typescript
|
|
9
|
+
* if (hasStdin()) {
|
|
10
|
+
* const input = await readStdin()
|
|
11
|
+
* // Process piped input
|
|
12
|
+
* } else {
|
|
13
|
+
* // Interactive mode - prompt user
|
|
14
|
+
* }
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Check if stdin has piped data available.
|
|
19
|
+
* Returns true when data is piped into the command, false in interactive terminals.
|
|
20
|
+
*
|
|
21
|
+
* @returns True if stdin is piped (not a TTY), false if interactive terminal
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```typescript
|
|
25
|
+
* // echo "data" | pai process
|
|
26
|
+
* hasStdin() // Returns true
|
|
27
|
+
*
|
|
28
|
+
* // pai process (no pipe)
|
|
29
|
+
* hasStdin() // Returns false
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare function hasStdin(): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Read all data from stdin.
|
|
35
|
+
* Reads piped input data and returns as UTF-8 string.
|
|
36
|
+
* Returns empty string if no stdin available.
|
|
37
|
+
*
|
|
38
|
+
* @returns Promise resolving to stdin content as string, or empty string if no stdin
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```typescript
|
|
42
|
+
* const input = await readStdin()
|
|
43
|
+
* if (input) {
|
|
44
|
+
* console.log(`Received: ${input}`)
|
|
45
|
+
* }
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export declare function readStdin(): Promise<string>;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* stdin Detection and Reading Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides utilities for detecting and reading piped input from stdin.
|
|
5
|
+
* Used by commands that need to accept piped data from other commands.
|
|
6
|
+
*
|
|
7
|
+
* @example Basic stdin detection
|
|
8
|
+
* ```typescript
|
|
9
|
+
* if (hasStdin()) {
|
|
10
|
+
* const input = await readStdin()
|
|
11
|
+
* // Process piped input
|
|
12
|
+
* } else {
|
|
13
|
+
* // Interactive mode - prompt user
|
|
14
|
+
* }
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
import { stdin } from 'node:process';
|
|
18
|
+
/**
|
|
19
|
+
* Check if stdin has piped data available.
|
|
20
|
+
* Returns true when data is piped into the command, false in interactive terminals.
|
|
21
|
+
*
|
|
22
|
+
* @returns True if stdin is piped (not a TTY), false if interactive terminal
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* // echo "data" | pai process
|
|
27
|
+
* hasStdin() // Returns true
|
|
28
|
+
*
|
|
29
|
+
* // pai process (no pipe)
|
|
30
|
+
* hasStdin() // Returns false
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export function hasStdin() {
|
|
34
|
+
return stdin.isTTY !== true;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Read all data from stdin.
|
|
38
|
+
* Reads piped input data and returns as UTF-8 string.
|
|
39
|
+
* Returns empty string if no stdin available.
|
|
40
|
+
*
|
|
41
|
+
* @returns Promise resolving to stdin content as string, or empty string if no stdin
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```typescript
|
|
45
|
+
* const input = await readStdin()
|
|
46
|
+
* if (input) {
|
|
47
|
+
* console.log(`Received: ${input}`)
|
|
48
|
+
* }
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export async function readStdin() {
|
|
52
|
+
if (!hasStdin()) {
|
|
53
|
+
return '';
|
|
54
|
+
}
|
|
55
|
+
const chunks = [];
|
|
56
|
+
for await (const chunk of stdin) {
|
|
57
|
+
chunks.push(chunk);
|
|
58
|
+
}
|
|
59
|
+
return Buffer.concat(chunks).toString('utf8');
|
|
60
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration for template installation
|
|
3
|
+
*/
|
|
4
|
+
export interface TemplateInstallConfig {
|
|
5
|
+
/** List of IDE names to install (e.g., ['claude', 'windsurf']) */
|
|
6
|
+
ides: string[];
|
|
7
|
+
/** Project name for configuration generation */
|
|
8
|
+
projectName: string;
|
|
9
|
+
/** Target directory where template will be installed */
|
|
10
|
+
targetDir: string;
|
|
11
|
+
/** Name of the template to install (e.g., 'bmad') */
|
|
12
|
+
templateName: string;
|
|
13
|
+
/** Absolute path to the template directory */
|
|
14
|
+
templatePath: string;
|
|
15
|
+
/** Username for configuration generation */
|
|
16
|
+
username: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Status of a single template item (file or folder)
|
|
20
|
+
*/
|
|
21
|
+
export interface TemplateItemStatus {
|
|
22
|
+
/** Whether the item exists in target directory */
|
|
23
|
+
exists: boolean;
|
|
24
|
+
/** Whether the item is a directory */
|
|
25
|
+
isDirectory: boolean;
|
|
26
|
+
/** The item name */
|
|
27
|
+
name: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Result of checking template installation status
|
|
31
|
+
*/
|
|
32
|
+
export interface TemplateInstallationStatus {
|
|
33
|
+
/** Items that already exist in target directory */
|
|
34
|
+
existing: TemplateItemStatus[];
|
|
35
|
+
/** Items that are missing from target directory */
|
|
36
|
+
missing: TemplateItemStatus[];
|
|
37
|
+
/** The method-specific workflow folder name (e.g., '_gsd', '_bmad') */
|
|
38
|
+
workflowFolder: null | string;
|
|
39
|
+
/** Whether the workflow folder exists */
|
|
40
|
+
workflowFolderExists: boolean;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Result of template installation
|
|
44
|
+
*/
|
|
45
|
+
export interface InstallationResult {
|
|
46
|
+
/** List of folder names that were installed (for gitignore) */
|
|
47
|
+
installedFolders: string[];
|
|
48
|
+
/** Number of files that were merged into existing folders */
|
|
49
|
+
mergedFileCount: number;
|
|
50
|
+
/** List of folder names that had content merged */
|
|
51
|
+
mergedFolders: string[];
|
|
52
|
+
/** Whether shared settings were merged into IDE settings */
|
|
53
|
+
sharedSettingsMerged: boolean;
|
|
54
|
+
/** List of folder names that were skipped (already exist) */
|
|
55
|
+
skippedFolders: string[];
|
|
56
|
+
/** Absolute path to the template that was installed */
|
|
57
|
+
templatePath: string;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Check template installation status for a method.
|
|
61
|
+
* Returns which items exist and which are missing.
|
|
62
|
+
*
|
|
63
|
+
* @param templatePath - Path to the template directory
|
|
64
|
+
* @param targetDir - Target directory to check
|
|
65
|
+
* @param ides - List of IDEs to check (for dot folders)
|
|
66
|
+
* @param templateName - Name of the template (for identifying workflow folder)
|
|
67
|
+
* @returns Status of template items
|
|
68
|
+
*/
|
|
69
|
+
export declare function checkTemplateStatus(templatePath: string, targetDir: string, ides: string[], templateName: string): Promise<TemplateInstallationStatus>;
|
|
70
|
+
/**
|
|
71
|
+
* Copy directory recursively with proper error handling.
|
|
72
|
+
* Excludes test files, cache directories, and output folders.
|
|
73
|
+
*
|
|
74
|
+
* @param src - Source directory path
|
|
75
|
+
* @param dest - Destination directory path
|
|
76
|
+
* @param excludeIdeFolders - If true, exclude IDE config folders (.claude, .windsurf, etc.)
|
|
77
|
+
*/
|
|
78
|
+
export declare function copyDir(src: string, dest: string, excludeIdeFolders?: boolean): Promise<void>;
|
|
79
|
+
/**
|
|
80
|
+
* Install template with IDE-specific folder selection.
|
|
81
|
+
* Supports selective installation - only installs items that don't already exist.
|
|
82
|
+
*
|
|
83
|
+
* Template structure:
|
|
84
|
+
* - Non-dot folders (e.g., _bmad/, GSR/) are installed if not already present
|
|
85
|
+
* - Dot folders (e.g., .claude/, .windsurf/) are installed only if matching IDE flag and not already present
|
|
86
|
+
*
|
|
87
|
+
* @param config - Installation configuration
|
|
88
|
+
* @param skipExisting - If true, skip items that already exist (default: true for regeneration support)
|
|
89
|
+
* @returns Installation result with list of installed and skipped folders
|
|
90
|
+
* @throws Error if template doesn't exist or requested IDE folder not found
|
|
91
|
+
*/
|
|
92
|
+
export declare function installTemplate(config: TemplateInstallConfig, skipExisting?: boolean): Promise<InstallationResult>;
|