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,107 @@
|
|
|
1
|
+
import BaseCommand from '../../lib/base-command.js';
|
|
2
|
+
/**
|
|
3
|
+
* Initialize AIW tools and integrations with specified template method.
|
|
4
|
+
*/
|
|
5
|
+
export default class Init extends BaseCommand {
|
|
6
|
+
static description: string;
|
|
7
|
+
static examples: string[];
|
|
8
|
+
static flags: {
|
|
9
|
+
interactive: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
method: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
ide: import("@oclif/core/interfaces").OptionFlag<string[], import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
debug: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
13
|
+
help: import("@oclif/core/interfaces").BooleanFlag<void>;
|
|
14
|
+
quiet: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
15
|
+
};
|
|
16
|
+
run(): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Copy directory recursively, excluding test files and cache
|
|
19
|
+
*
|
|
20
|
+
* @param src - Source directory path
|
|
21
|
+
* @param dest - Destination directory path
|
|
22
|
+
* @param excludeIdeFolders - If true, exclude IDE config folders (.claude, .windsurf, etc.)
|
|
23
|
+
*/
|
|
24
|
+
private copyDirectory;
|
|
25
|
+
/**
|
|
26
|
+
* Get description for a template
|
|
27
|
+
*
|
|
28
|
+
* @param template - Template name
|
|
29
|
+
* @returns Template description
|
|
30
|
+
*/
|
|
31
|
+
private getTemplateDescription;
|
|
32
|
+
/**
|
|
33
|
+
* Merge settings from multiple method templates into project settings.
|
|
34
|
+
* Processes methods in order, allowing later methods to override earlier ones.
|
|
35
|
+
*
|
|
36
|
+
* @param targetDir - Project directory
|
|
37
|
+
* @param methods - Array of method names to merge (e.g., ['_shared', 'cc-native'])
|
|
38
|
+
* @param ides - IDEs being configured (for IDE-specific merging)
|
|
39
|
+
*/
|
|
40
|
+
private mergeMethodsSettings;
|
|
41
|
+
/**
|
|
42
|
+
* Merge Windsurf template hooks into project hooks
|
|
43
|
+
*
|
|
44
|
+
* @param targetDir - Project directory
|
|
45
|
+
* @param templatePath - Template source path
|
|
46
|
+
*/
|
|
47
|
+
private mergeWindsurfTemplateHooks;
|
|
48
|
+
/**
|
|
49
|
+
* Check if a path exists
|
|
50
|
+
*/
|
|
51
|
+
private pathExists;
|
|
52
|
+
/**
|
|
53
|
+
* Perform post-installation actions.
|
|
54
|
+
*
|
|
55
|
+
* Handles:
|
|
56
|
+
* - Method tracking in settings.json
|
|
57
|
+
* - Settings/hooks merging for all methods
|
|
58
|
+
* - .gitignore updates
|
|
59
|
+
*
|
|
60
|
+
* @param config - Post-install configuration
|
|
61
|
+
* @param config.targetDir - Project directory
|
|
62
|
+
* @param config.method - Method name that was installed
|
|
63
|
+
* @param config.ides - IDEs that were configured
|
|
64
|
+
* @param config.hasGit - Whether git repository exists
|
|
65
|
+
* @param config.foldersForGitignore - Folders to add to .gitignore
|
|
66
|
+
*/
|
|
67
|
+
private performPostInstallActions;
|
|
68
|
+
/**
|
|
69
|
+
* Resolve installation configuration from flags or interactive wizard.
|
|
70
|
+
*
|
|
71
|
+
* Determines what to install based on:
|
|
72
|
+
* - Interactive wizard input
|
|
73
|
+
* - Command-line flags
|
|
74
|
+
* - Minimal install mode (no method specified)
|
|
75
|
+
*
|
|
76
|
+
* @param flags - Parsed command flags
|
|
77
|
+
* @param flags.interactive - Run interactive wizard
|
|
78
|
+
* @param flags.method - Template method to install
|
|
79
|
+
* @param flags.ide - IDEs to configure
|
|
80
|
+
* @param targetDir - Target directory for installation
|
|
81
|
+
* @param availableTemplates - List of available template names
|
|
82
|
+
* @returns Installation configuration or null for minimal install
|
|
83
|
+
*/
|
|
84
|
+
private resolveInstallationConfig;
|
|
85
|
+
/**
|
|
86
|
+
* Run interactive setup wizard
|
|
87
|
+
*
|
|
88
|
+
* @param targetDir - Target directory for installation
|
|
89
|
+
* @param availableTemplates - List of available template names
|
|
90
|
+
* @returns Wizard configuration result
|
|
91
|
+
*/
|
|
92
|
+
private runInteractiveWizard;
|
|
93
|
+
/**
|
|
94
|
+
* Check if a file should be excluded from copying
|
|
95
|
+
*/
|
|
96
|
+
private shouldExcludeFile;
|
|
97
|
+
/**
|
|
98
|
+
* Track method installation in settings.json
|
|
99
|
+
*
|
|
100
|
+
* Adds method entry to the methods object with installation metadata.
|
|
101
|
+
*
|
|
102
|
+
* @param targetDir - Project directory
|
|
103
|
+
* @param method - Method name being installed
|
|
104
|
+
* @param ides - IDEs configured for this method
|
|
105
|
+
*/
|
|
106
|
+
private trackMethodInstallation;
|
|
107
|
+
}
|
|
@@ -0,0 +1,565 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
import { basename, dirname, join } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { checkbox, confirm, input, select } from '@inquirer/prompts';
|
|
5
|
+
import { Flags } from '@oclif/core';
|
|
6
|
+
import BaseCommand from '../../lib/base-command.js';
|
|
7
|
+
import { detectUsername } from '../../lib/user-utils.js';
|
|
8
|
+
import { updateGitignore } from '../../lib/gitignore-manager.js';
|
|
9
|
+
import { mergeClaudeSettings } from '../../lib/hooks-merger.js';
|
|
10
|
+
import { IdePathResolver } from '../../lib/ide-path-resolver.js';
|
|
11
|
+
import { getTargetSettingsFile, readClaudeSettings, writeClaudeSettings } from '../../lib/settings-hierarchy.js';
|
|
12
|
+
import { checkTemplateStatus, installTemplate } from '../../lib/template-installer.js';
|
|
13
|
+
import { getAvailableTemplates, getTemplatePath } from '../../lib/template-resolver.js';
|
|
14
|
+
import { getTargetHooksFile, readWindsurfHooks, writeWindsurfHooks } from '../../lib/windsurf-hooks-hierarchy.js';
|
|
15
|
+
import { mergeWindsurfHooks } from '../../lib/windsurf-hooks-merger.js';
|
|
16
|
+
import { EXIT_CODES } from '../../types/exit-codes.js';
|
|
17
|
+
/**
|
|
18
|
+
* Available IDEs for configuration
|
|
19
|
+
*/
|
|
20
|
+
const AVAILABLE_IDES = [
|
|
21
|
+
{ value: 'claude', name: 'Claude Code', description: 'Anthropic Claude Code CLI' },
|
|
22
|
+
{ value: 'windsurf', name: 'Windsurf', description: 'Codeium Windsurf IDE' },
|
|
23
|
+
];
|
|
24
|
+
/**
|
|
25
|
+
* Detect if current directory is a git repository.
|
|
26
|
+
* Checks for .git directory existence.
|
|
27
|
+
*/
|
|
28
|
+
async function detectGitRepository(targetDir) {
|
|
29
|
+
try {
|
|
30
|
+
const gitPath = join(targetDir, '.git');
|
|
31
|
+
await fs.access(gitPath);
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Extract project name from directory path.
|
|
40
|
+
* Returns the basename of the given directory.
|
|
41
|
+
*/
|
|
42
|
+
function detectProjectName(targetDir) {
|
|
43
|
+
return basename(targetDir);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Initialize AIW tools and integrations with specified template method.
|
|
47
|
+
*/
|
|
48
|
+
export default class Init extends BaseCommand {
|
|
49
|
+
static description = 'Initialize AIW tools and integrations with specified template method';
|
|
50
|
+
static examples = [
|
|
51
|
+
'<%= config.bin %> <%= command.id %> --interactive',
|
|
52
|
+
'<%= config.bin %> <%= command.id %> --method cc-native',
|
|
53
|
+
'<%= config.bin %> <%= command.id %> --method cc-native --ide windsurf',
|
|
54
|
+
'<%= config.bin %> <%= command.id %> --method cc-native --ide claude --ide windsurf',
|
|
55
|
+
];
|
|
56
|
+
static flags = {
|
|
57
|
+
...BaseCommand.baseFlags,
|
|
58
|
+
interactive: Flags.boolean({
|
|
59
|
+
char: 'I',
|
|
60
|
+
description: 'Run interactive setup wizard',
|
|
61
|
+
default: false,
|
|
62
|
+
}),
|
|
63
|
+
method: Flags.string({
|
|
64
|
+
char: 'm',
|
|
65
|
+
description: 'Template method to initialize',
|
|
66
|
+
required: false,
|
|
67
|
+
}),
|
|
68
|
+
ide: Flags.string({
|
|
69
|
+
char: 'i',
|
|
70
|
+
default: ['claude'],
|
|
71
|
+
description: 'IDEs to configure (specify multiple: --ide claude --ide windsurf)',
|
|
72
|
+
multiple: true,
|
|
73
|
+
}),
|
|
74
|
+
};
|
|
75
|
+
async run() {
|
|
76
|
+
const { flags } = await this.parse(Init);
|
|
77
|
+
const targetDir = process.cwd();
|
|
78
|
+
try {
|
|
79
|
+
// Get available templates for validation
|
|
80
|
+
const availableTemplates = await getAvailableTemplates();
|
|
81
|
+
// Check git repository early (needed by both install paths)
|
|
82
|
+
const hasGit = await detectGitRepository(targetDir);
|
|
83
|
+
// Resolve installation configuration from flags or interactive wizard
|
|
84
|
+
const config = await this.resolveInstallationConfig(flags, targetDir, availableTemplates);
|
|
85
|
+
// If config is null, perform minimal install (shared folder only)
|
|
86
|
+
if (!config) {
|
|
87
|
+
this.logInfo('Performing minimal installation (_shared folder only)...');
|
|
88
|
+
this.log('');
|
|
89
|
+
// Create .aiwcli container and install _shared
|
|
90
|
+
const resolver = new IdePathResolver(targetDir);
|
|
91
|
+
const containerDir = resolver.getAiwcliContainer();
|
|
92
|
+
await fs.mkdir(containerDir, { recursive: true });
|
|
93
|
+
const sharedDestPath = resolver.getSharedFolder();
|
|
94
|
+
const sharedExists = await this.pathExists(sharedDestPath);
|
|
95
|
+
if (!sharedExists) {
|
|
96
|
+
const currentFilePath = fileURLToPath(import.meta.url);
|
|
97
|
+
const currentDir = dirname(currentFilePath);
|
|
98
|
+
const templatesRoot = join(dirname(dirname(currentDir)), 'templates');
|
|
99
|
+
const sharedSrcPath = join(templatesRoot, '_shared');
|
|
100
|
+
if (!(await this.pathExists(sharedSrcPath))) {
|
|
101
|
+
this.error(`Shared folder not found at ${sharedSrcPath}. This indicates a corrupted installation.`, {
|
|
102
|
+
exit: EXIT_CODES.ENVIRONMENT_ERROR,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
await this.copyDirectory(sharedSrcPath, sharedDestPath, true);
|
|
106
|
+
this.logSuccess('✓ Installed _shared folder');
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
this.logInfo('✓ _shared folder already exists - skipping');
|
|
110
|
+
}
|
|
111
|
+
// Merge settings from _shared template
|
|
112
|
+
await this.mergeMethodsSettings(targetDir, ['_shared'], ['claude']);
|
|
113
|
+
// Update .gitignore if git repository exists
|
|
114
|
+
if (hasGit) {
|
|
115
|
+
await updateGitignore(targetDir, ['.aiwcli']);
|
|
116
|
+
this.logSuccess('✓ .gitignore updated');
|
|
117
|
+
}
|
|
118
|
+
this.log('');
|
|
119
|
+
this.logSuccess('✓ Minimal installation completed successfully');
|
|
120
|
+
this.log('');
|
|
121
|
+
this.logInfo('Next steps:');
|
|
122
|
+
this.logInfo(' aiw init --method <template> Install a full template method (cc-native)');
|
|
123
|
+
this.logInfo(' aiw init --interactive Run interactive setup wizard');
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const { method, ides, username, projectName } = config;
|
|
127
|
+
// Validate write permissions
|
|
128
|
+
try {
|
|
129
|
+
const testFile = join(targetDir, '.aiwcli-write-test');
|
|
130
|
+
await fs.writeFile(testFile, '', 'utf8');
|
|
131
|
+
await fs.unlink(testFile);
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
this.error('Permission denied. Cannot write to current directory.', {
|
|
135
|
+
exit: EXIT_CODES.ENVIRONMENT_ERROR,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
// Get template path
|
|
139
|
+
const templatePath = await getTemplatePath(method);
|
|
140
|
+
// Check what already exists vs what's missing
|
|
141
|
+
const status = await checkTemplateStatus(templatePath, targetDir, ides, method);
|
|
142
|
+
this.logInfo(`Installing ${method} template for project: ${projectName}`);
|
|
143
|
+
this.logInfo(`Detected user: ${username}`);
|
|
144
|
+
this.logInfo(`Target IDEs: ${ides.join(', ')}`);
|
|
145
|
+
// Report existing items
|
|
146
|
+
if (status.existing.length > 0) {
|
|
147
|
+
this.log('');
|
|
148
|
+
this.logInfo('Already present (will be skipped):');
|
|
149
|
+
for (const item of status.existing) {
|
|
150
|
+
const suffix = item.isDirectory ? '/' : '';
|
|
151
|
+
this.log(` - ${item.name}${suffix}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// Report missing items that will be installed
|
|
155
|
+
if (status.missing.length > 0) {
|
|
156
|
+
this.log('');
|
|
157
|
+
this.logInfo('Will be installed:');
|
|
158
|
+
for (const item of status.missing) {
|
|
159
|
+
const suffix = item.isDirectory ? '/' : '';
|
|
160
|
+
this.log(` - ${item.name}${suffix}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
// If everything already exists, report and continue (don't block)
|
|
164
|
+
if (status.missing.length === 0) {
|
|
165
|
+
this.log('');
|
|
166
|
+
this.logInfo('All template items already exist. Nothing new to install.');
|
|
167
|
+
this.log('');
|
|
168
|
+
// Still update gitignore and merge hooks if needed
|
|
169
|
+
}
|
|
170
|
+
this.log('');
|
|
171
|
+
// Install template with selective installation (skip existing items)
|
|
172
|
+
const result = await installTemplate({
|
|
173
|
+
templateName: method,
|
|
174
|
+
targetDir,
|
|
175
|
+
ides,
|
|
176
|
+
username,
|
|
177
|
+
projectName,
|
|
178
|
+
templatePath,
|
|
179
|
+
}, true);
|
|
180
|
+
// Collect all folders that need gitignore entries
|
|
181
|
+
// The .aiwcli/ container holds all template infrastructure and runtime data
|
|
182
|
+
const foldersForGitignore = ['.aiwcli'];
|
|
183
|
+
// Report installation results
|
|
184
|
+
if (result.installedFolders.length > 0) {
|
|
185
|
+
this.logSuccess(`✓ Installed: ${result.installedFolders.join(', ')}`);
|
|
186
|
+
}
|
|
187
|
+
if (result.mergedFolders.length > 0) {
|
|
188
|
+
this.logSuccess(`✓ Merged content into: ${result.mergedFolders.join(', ')} (${result.mergedFileCount} files)`);
|
|
189
|
+
}
|
|
190
|
+
if (result.skippedFolders.length > 0) {
|
|
191
|
+
this.logInfo(`✓ Skipped (already exist): ${result.skippedFolders.join(', ')}`);
|
|
192
|
+
}
|
|
193
|
+
// Perform post-installation actions (settings tracking, hook merging, gitignore updates)
|
|
194
|
+
await this.performPostInstallActions({
|
|
195
|
+
targetDir,
|
|
196
|
+
method,
|
|
197
|
+
ides,
|
|
198
|
+
hasGit,
|
|
199
|
+
foldersForGitignore,
|
|
200
|
+
});
|
|
201
|
+
this.log('');
|
|
202
|
+
this.logSuccess(`✓ ${method} initialized successfully`);
|
|
203
|
+
this.log('');
|
|
204
|
+
this.logInfo('Next steps:');
|
|
205
|
+
this.logInfo(' aiw launch Start Claude Code with agents');
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
const err = error;
|
|
209
|
+
// Categorize errors for better user feedback
|
|
210
|
+
// Check error codes first, then fall back to message matching
|
|
211
|
+
if (err.code === 'EACCES' || err.code === 'EPERM') {
|
|
212
|
+
this.error(`Permission denied. Cannot write to current directory. ${err.message}`, {
|
|
213
|
+
exit: EXIT_CODES.ENVIRONMENT_ERROR,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
if (err.code === 'ENOENT' || err.message?.includes('not found') || err.message?.includes('not available')) {
|
|
217
|
+
this.error(err.message || 'Resource not found', { exit: EXIT_CODES.INVALID_USAGE });
|
|
218
|
+
}
|
|
219
|
+
// Generic error fallback
|
|
220
|
+
this.error(`Installation failed: ${err.message}`, {
|
|
221
|
+
exit: EXIT_CODES.GENERAL_ERROR,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Copy directory recursively, excluding test files and cache
|
|
227
|
+
*
|
|
228
|
+
* @param src - Source directory path
|
|
229
|
+
* @param dest - Destination directory path
|
|
230
|
+
* @param excludeIdeFolders - If true, exclude IDE config folders (.claude, .windsurf, etc.)
|
|
231
|
+
*/
|
|
232
|
+
async copyDirectory(src, dest, excludeIdeFolders = false) {
|
|
233
|
+
await fs.mkdir(dest, { recursive: true });
|
|
234
|
+
const entries = await fs.readdir(src, { withFileTypes: true });
|
|
235
|
+
const operations = entries
|
|
236
|
+
.filter((entry) => {
|
|
237
|
+
// Standard exclusions (test files, cache, etc.)
|
|
238
|
+
if (this.shouldExcludeFile(entry.name)) {
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
// Exclude IDE config folders if requested (used for _shared folder)
|
|
242
|
+
// These folders are used for settings merging, not direct installation
|
|
243
|
+
if (excludeIdeFolders && entry.isDirectory() && entry.name.startsWith('.')) {
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
return true;
|
|
247
|
+
})
|
|
248
|
+
.map(async (entry) => {
|
|
249
|
+
const srcPath = join(src, entry.name);
|
|
250
|
+
const destPath = join(dest, entry.name);
|
|
251
|
+
try {
|
|
252
|
+
return entry.isDirectory() ? await this.copyDirectory(srcPath, destPath, excludeIdeFolders) : await fs.copyFile(srcPath, destPath);
|
|
253
|
+
}
|
|
254
|
+
catch (error) {
|
|
255
|
+
const err = error;
|
|
256
|
+
throw new Error(`Failed to copy ${srcPath} to ${destPath}: ${err.message}`);
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
await Promise.all(operations);
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Get description for a template
|
|
263
|
+
*
|
|
264
|
+
* @param template - Template name
|
|
265
|
+
* @returns Template description
|
|
266
|
+
*/
|
|
267
|
+
getTemplateDescription(template) {
|
|
268
|
+
const descriptions = {
|
|
269
|
+
'cc-native': 'CC-Native - Event-sourced context management with plan review',
|
|
270
|
+
};
|
|
271
|
+
return descriptions[template] || 'Custom template';
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Merge settings from multiple method templates into project settings.
|
|
275
|
+
* Processes methods in order, allowing later methods to override earlier ones.
|
|
276
|
+
*
|
|
277
|
+
* @param targetDir - Project directory
|
|
278
|
+
* @param methods - Array of method names to merge (e.g., ['_shared', 'cc-native'])
|
|
279
|
+
* @param ides - IDEs being configured (for IDE-specific merging)
|
|
280
|
+
*/
|
|
281
|
+
async mergeMethodsSettings(targetDir, methods, ides) {
|
|
282
|
+
const targetSettingsPath = getTargetSettingsFile(targetDir);
|
|
283
|
+
let projectSettings = (await readClaudeSettings(targetSettingsPath)) || {};
|
|
284
|
+
for (const method of methods) {
|
|
285
|
+
try {
|
|
286
|
+
// Get template path for this method
|
|
287
|
+
let templatePath;
|
|
288
|
+
if (method === '_shared') {
|
|
289
|
+
// Special case: _shared is at templates/_shared
|
|
290
|
+
const currentFilePath = fileURLToPath(import.meta.url);
|
|
291
|
+
const currentDir = dirname(currentFilePath);
|
|
292
|
+
const templatesRoot = join(dirname(dirname(currentDir)), 'templates');
|
|
293
|
+
templatePath = join(templatesRoot, '_shared');
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
// Named method templates
|
|
297
|
+
templatePath = await getTemplatePath(method);
|
|
298
|
+
}
|
|
299
|
+
// Merge Claude settings if claude IDE is selected
|
|
300
|
+
if (ides.includes('claude')) {
|
|
301
|
+
const templateSettingsPath = join(templatePath, '.claude', 'settings.json');
|
|
302
|
+
const templateSettings = await readClaudeSettings(templateSettingsPath);
|
|
303
|
+
if (templateSettings) {
|
|
304
|
+
projectSettings = mergeClaudeSettings(projectSettings, templateSettings);
|
|
305
|
+
this.logSuccess(`✓ Merged ${method} settings into .claude/settings.json`);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
// Merge Windsurf hooks if windsurf IDE is selected
|
|
309
|
+
if (ides.includes('windsurf')) {
|
|
310
|
+
await this.mergeWindsurfTemplateHooks(targetDir, templatePath);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
catch (error) {
|
|
314
|
+
const err = error;
|
|
315
|
+
this.warn(`Failed to merge ${method} settings: ${err.message}`);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
// Write merged Claude settings
|
|
319
|
+
if (ides.includes('claude')) {
|
|
320
|
+
await writeClaudeSettings(targetSettingsPath, projectSettings);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Merge Windsurf template hooks into project hooks
|
|
325
|
+
*
|
|
326
|
+
* @param targetDir - Project directory
|
|
327
|
+
* @param templatePath - Template source path
|
|
328
|
+
*/
|
|
329
|
+
async mergeWindsurfTemplateHooks(targetDir, templatePath) {
|
|
330
|
+
try {
|
|
331
|
+
// Read template hooks
|
|
332
|
+
const templateHooksPath = join(templatePath, '.windsurf', 'hooks.json');
|
|
333
|
+
const templateHooks = await readWindsurfHooks(templateHooksPath);
|
|
334
|
+
// If template has no hooks, nothing to merge
|
|
335
|
+
if (!templateHooks || !templateHooks.hooks || Object.keys(templateHooks.hooks).length === 0) {
|
|
336
|
+
this.logInfo('No Windsurf hooks in template to merge');
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
// Get target hooks file path
|
|
340
|
+
const targetHooksPath = getTargetHooksFile(targetDir);
|
|
341
|
+
// Read existing project hooks
|
|
342
|
+
const existingHooks = await readWindsurfHooks(targetHooksPath);
|
|
343
|
+
// Merge hooks
|
|
344
|
+
const mergedHooks = mergeWindsurfHooks(existingHooks, templateHooks);
|
|
345
|
+
// Write merged hooks
|
|
346
|
+
await writeWindsurfHooks(targetHooksPath, mergedHooks);
|
|
347
|
+
this.logSuccess('✓ Windsurf template hooks merged into project hooks');
|
|
348
|
+
}
|
|
349
|
+
catch (error) {
|
|
350
|
+
const err = error;
|
|
351
|
+
this.warn(`Failed to merge Windsurf template hooks: ${err.message}`);
|
|
352
|
+
// Don't fail the entire installation if hook merging fails
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Check if a path exists
|
|
357
|
+
*/
|
|
358
|
+
async pathExists(path) {
|
|
359
|
+
try {
|
|
360
|
+
await fs.access(path);
|
|
361
|
+
return true;
|
|
362
|
+
}
|
|
363
|
+
catch {
|
|
364
|
+
return false;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Perform post-installation actions.
|
|
369
|
+
*
|
|
370
|
+
* Handles:
|
|
371
|
+
* - Method tracking in settings.json
|
|
372
|
+
* - Settings/hooks merging for all methods
|
|
373
|
+
* - .gitignore updates
|
|
374
|
+
*
|
|
375
|
+
* @param config - Post-install configuration
|
|
376
|
+
* @param config.targetDir - Project directory
|
|
377
|
+
* @param config.method - Method name that was installed
|
|
378
|
+
* @param config.ides - IDEs that were configured
|
|
379
|
+
* @param config.hasGit - Whether git repository exists
|
|
380
|
+
* @param config.foldersForGitignore - Folders to add to .gitignore
|
|
381
|
+
*/
|
|
382
|
+
async performPostInstallActions(config) {
|
|
383
|
+
const { targetDir, method, ides, hasGit, foldersForGitignore } = config;
|
|
384
|
+
// Track method installation in settings.json
|
|
385
|
+
await this.trackMethodInstallation(targetDir, method, ides);
|
|
386
|
+
// Merge settings from _shared and method templates
|
|
387
|
+
await this.mergeMethodsSettings(targetDir, ['_shared', method], ides);
|
|
388
|
+
// Update .gitignore if git repository exists
|
|
389
|
+
if (hasGit) {
|
|
390
|
+
await updateGitignore(targetDir, foldersForGitignore);
|
|
391
|
+
this.logSuccess('✓ .gitignore updated');
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Resolve installation configuration from flags or interactive wizard.
|
|
396
|
+
*
|
|
397
|
+
* Determines what to install based on:
|
|
398
|
+
* - Interactive wizard input
|
|
399
|
+
* - Command-line flags
|
|
400
|
+
* - Minimal install mode (no method specified)
|
|
401
|
+
*
|
|
402
|
+
* @param flags - Parsed command flags
|
|
403
|
+
* @param flags.interactive - Run interactive wizard
|
|
404
|
+
* @param flags.method - Template method to install
|
|
405
|
+
* @param flags.ide - IDEs to configure
|
|
406
|
+
* @param targetDir - Target directory for installation
|
|
407
|
+
* @param availableTemplates - List of available template names
|
|
408
|
+
* @returns Installation configuration or null for minimal install
|
|
409
|
+
*/
|
|
410
|
+
async resolveInstallationConfig(flags, targetDir, availableTemplates) {
|
|
411
|
+
if (flags.interactive) {
|
|
412
|
+
// Run interactive wizard
|
|
413
|
+
const wizardResult = await this.runInteractiveWizard(targetDir, availableTemplates);
|
|
414
|
+
if (!wizardResult.confirmed) {
|
|
415
|
+
this.log('Installation cancelled.');
|
|
416
|
+
return null;
|
|
417
|
+
}
|
|
418
|
+
return {
|
|
419
|
+
method: wizardResult.method,
|
|
420
|
+
ides: wizardResult.ides,
|
|
421
|
+
username: wizardResult.username,
|
|
422
|
+
projectName: wizardResult.projectName,
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
if (flags.method) {
|
|
426
|
+
// Use flags (method specified)
|
|
427
|
+
// Validate template exists
|
|
428
|
+
if (!availableTemplates.includes(flags.method)) {
|
|
429
|
+
this.error(`Template '${flags.method}' not found. Available templates: ${availableTemplates.join(', ')}`, {
|
|
430
|
+
exit: EXIT_CODES.INVALID_USAGE,
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
return {
|
|
434
|
+
method: flags.method,
|
|
435
|
+
ides: flags.ide,
|
|
436
|
+
username: await detectUsername(),
|
|
437
|
+
projectName: detectProjectName(targetDir),
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
// Minimal install mode - install only _shared folder
|
|
441
|
+
return null;
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Run interactive setup wizard
|
|
445
|
+
*
|
|
446
|
+
* @param targetDir - Target directory for installation
|
|
447
|
+
* @param availableTemplates - List of available template names
|
|
448
|
+
* @returns Wizard configuration result
|
|
449
|
+
*/
|
|
450
|
+
async runInteractiveWizard(targetDir, availableTemplates) {
|
|
451
|
+
this.log('');
|
|
452
|
+
this.log('┌─────────────────────────────────────────┐');
|
|
453
|
+
this.log('│ AIW Interactive Setup Wizard │');
|
|
454
|
+
this.log('└─────────────────────────────────────────┘');
|
|
455
|
+
this.log('');
|
|
456
|
+
// Detect defaults
|
|
457
|
+
const detectedUsername = await detectUsername();
|
|
458
|
+
const detectedProjectName = detectProjectName(targetDir);
|
|
459
|
+
// Step 1: Select template method
|
|
460
|
+
const method = await select({
|
|
461
|
+
message: 'Select a template method:',
|
|
462
|
+
choices: availableTemplates.map((template) => ({
|
|
463
|
+
value: template,
|
|
464
|
+
name: template.toUpperCase(),
|
|
465
|
+
description: this.getTemplateDescription(template),
|
|
466
|
+
})),
|
|
467
|
+
});
|
|
468
|
+
this.log('');
|
|
469
|
+
// Step 2: Select IDEs
|
|
470
|
+
const ides = await checkbox({
|
|
471
|
+
message: 'Select IDEs to configure:',
|
|
472
|
+
choices: AVAILABLE_IDES.map((ide) => ({
|
|
473
|
+
value: ide.value,
|
|
474
|
+
name: ide.name,
|
|
475
|
+
checked: ide.value === 'claude', // Default to Claude selected
|
|
476
|
+
})),
|
|
477
|
+
required: true,
|
|
478
|
+
});
|
|
479
|
+
this.log('');
|
|
480
|
+
// Step 3: Confirm/edit username
|
|
481
|
+
const username = await input({
|
|
482
|
+
message: 'Username:',
|
|
483
|
+
default: detectedUsername,
|
|
484
|
+
});
|
|
485
|
+
// Step 4: Confirm/edit project name
|
|
486
|
+
const projectName = await input({
|
|
487
|
+
message: 'Project name:',
|
|
488
|
+
default: detectedProjectName,
|
|
489
|
+
});
|
|
490
|
+
this.log('');
|
|
491
|
+
// Step 5: Summary and confirmation
|
|
492
|
+
this.log('┌─────────────────────────────────────────┐');
|
|
493
|
+
this.log('│ Installation Summary │');
|
|
494
|
+
this.log('├─────────────────────────────────────────┤');
|
|
495
|
+
this.log(`│ Template: ${method.padEnd(24)}│`);
|
|
496
|
+
this.log(`│ IDEs: ${ides.join(', ').padEnd(24)}│`);
|
|
497
|
+
this.log(`│ Username: ${username.padEnd(24)}│`);
|
|
498
|
+
this.log(`│ Project: ${projectName.padEnd(24)}│`);
|
|
499
|
+
this.log(`│ Directory: ${basename(targetDir).padEnd(24)}│`);
|
|
500
|
+
this.log('└─────────────────────────────────────────┘');
|
|
501
|
+
this.log('');
|
|
502
|
+
const confirmed = await confirm({
|
|
503
|
+
message: 'Proceed with installation?',
|
|
504
|
+
default: true,
|
|
505
|
+
});
|
|
506
|
+
return {
|
|
507
|
+
method,
|
|
508
|
+
ides,
|
|
509
|
+
username,
|
|
510
|
+
projectName,
|
|
511
|
+
confirmed,
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
/**
|
|
515
|
+
* Check if a file should be excluded from copying
|
|
516
|
+
*/
|
|
517
|
+
shouldExcludeFile(name) {
|
|
518
|
+
const excludedPatterns = [
|
|
519
|
+
'_output',
|
|
520
|
+
'__pycache__',
|
|
521
|
+
'.pytest_cache',
|
|
522
|
+
'conftest.py',
|
|
523
|
+
/^test_.*\.py$/,
|
|
524
|
+
/.*\.pyc$/,
|
|
525
|
+
];
|
|
526
|
+
return excludedPatterns.some((pattern) => {
|
|
527
|
+
if (typeof pattern === 'string') {
|
|
528
|
+
return name === pattern;
|
|
529
|
+
}
|
|
530
|
+
return pattern.test(name);
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* Track method installation in settings.json
|
|
535
|
+
*
|
|
536
|
+
* Adds method entry to the methods object with installation metadata.
|
|
537
|
+
*
|
|
538
|
+
* @param targetDir - Project directory
|
|
539
|
+
* @param method - Method name being installed
|
|
540
|
+
* @param ides - IDEs configured for this method
|
|
541
|
+
*/
|
|
542
|
+
async trackMethodInstallation(targetDir, method, ides) {
|
|
543
|
+
try {
|
|
544
|
+
const settingsPath = getTargetSettingsFile(targetDir);
|
|
545
|
+
const existingSettings = (await readClaudeSettings(settingsPath)) || {};
|
|
546
|
+
// Add method tracking
|
|
547
|
+
const updatedSettings = {
|
|
548
|
+
...existingSettings,
|
|
549
|
+
methods: {
|
|
550
|
+
...existingSettings.methods,
|
|
551
|
+
[method]: {
|
|
552
|
+
ides,
|
|
553
|
+
installedAt: new Date().toISOString(),
|
|
554
|
+
},
|
|
555
|
+
},
|
|
556
|
+
};
|
|
557
|
+
await writeClaudeSettings(settingsPath, updatedSettings);
|
|
558
|
+
this.logSuccess(`✓ Method '${method}' tracked in settings.json`);
|
|
559
|
+
}
|
|
560
|
+
catch (error) {
|
|
561
|
+
const err = error;
|
|
562
|
+
this.warn(`Failed to track method installation: ${err.message}`);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
}
|