aiwcli 0.10.2 → 0.11.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/bin/run.js +1 -1
- package/dist/commands/clear.d.ts +11 -6
- package/dist/commands/clear.js +229 -381
- package/dist/commands/init/index.d.ts +1 -17
- package/dist/commands/init/index.js +22 -107
- package/dist/lib/gitignore-manager.d.ts +32 -0
- package/dist/lib/gitignore-manager.js +141 -2
- package/dist/lib/template-installer.d.ts +7 -12
- package/dist/lib/template-installer.js +69 -193
- package/dist/lib/template-settings-reconstructor.d.ts +35 -0
- package/dist/lib/template-settings-reconstructor.js +130 -0
- package/dist/templates/CLAUDE.md +8 -8
- package/dist/templates/_shared/.claude/commands/handoff-resume.md +64 -0
- package/dist/templates/_shared/.claude/commands/handoff.md +16 -10
- package/dist/templates/_shared/.claude/settings.json +7 -7
- package/dist/templates/_shared/hooks-ts/_utils/git-state.ts +2 -0
- package/dist/templates/_shared/hooks-ts/archive_plan.ts +159 -0
- package/dist/templates/_shared/hooks-ts/context_monitor.ts +147 -0
- package/dist/templates/_shared/hooks-ts/file-suggestion.ts +130 -0
- package/dist/templates/_shared/hooks-ts/pre_compact.ts +49 -0
- package/dist/templates/_shared/hooks-ts/session_end.ts +104 -0
- package/dist/templates/_shared/hooks-ts/session_start.ts +144 -0
- package/dist/templates/_shared/hooks-ts/task_create_capture.ts +48 -0
- package/dist/templates/_shared/hooks-ts/task_update_capture.ts +74 -0
- package/dist/templates/_shared/hooks-ts/user_prompt_submit.ts +83 -0
- package/dist/templates/_shared/lib-ts/CLAUDE.md +318 -0
- package/dist/templates/_shared/lib-ts/base/atomic-write.ts +138 -0
- package/dist/templates/_shared/lib-ts/base/constants.ts +306 -0
- package/dist/templates/_shared/lib-ts/base/git-state.ts +58 -0
- package/dist/templates/_shared/lib-ts/base/hook-utils.ts +439 -0
- package/dist/templates/_shared/lib-ts/base/inference.ts +252 -0
- package/dist/templates/_shared/lib-ts/base/logger.ts +250 -0
- package/dist/templates/_shared/lib-ts/base/state-io.ts +116 -0
- package/dist/templates/_shared/lib-ts/base/stop-words.ts +184 -0
- package/dist/templates/_shared/lib-ts/base/subprocess-utils.ts +162 -0
- package/dist/templates/_shared/lib-ts/base/utils.ts +184 -0
- package/dist/templates/_shared/lib-ts/context/context-formatter.ts +438 -0
- package/dist/templates/_shared/lib-ts/context/context-selector.ts +515 -0
- package/dist/templates/_shared/lib-ts/context/context-store.ts +707 -0
- package/dist/templates/_shared/lib-ts/context/plan-manager.ts +316 -0
- package/dist/templates/_shared/lib-ts/context/task-tracker.ts +185 -0
- package/dist/templates/_shared/lib-ts/handoff/document-generator.ts +216 -0
- package/dist/templates/_shared/lib-ts/handoff/handoff-reader.ts +159 -0
- package/dist/templates/_shared/lib-ts/package.json +21 -0
- package/dist/templates/_shared/lib-ts/templates/formatters.ts +104 -0
- package/dist/templates/_shared/{lib/templates/plan_context.py → lib-ts/templates/plan-context.ts} +14 -22
- package/dist/templates/_shared/lib-ts/tsconfig.json +13 -0
- package/dist/templates/_shared/lib-ts/types.ts +164 -0
- package/dist/templates/_shared/scripts/resolve_context.ts +24 -0
- package/dist/templates/_shared/scripts/resume_handoff.ts +321 -0
- package/dist/templates/_shared/scripts/save_handoff.ts +359 -0
- package/dist/templates/_shared/scripts/status_line.ts +733 -0
- package/dist/templates/cc-native/.claude/settings.json +175 -185
- package/dist/templates/cc-native/TEMPLATE-SCHEMA.md +15 -17
- package/dist/templates/cc-native/_cc-native/agents/ARCH-EVOLUTION.md +63 -0
- package/dist/templates/cc-native/_cc-native/agents/ARCH-PATTERNS.md +62 -0
- package/dist/templates/cc-native/_cc-native/agents/ARCH-STRUCTURE.md +63 -0
- package/dist/templates/cc-native/_cc-native/agents/{ASSUMPTION-CHAIN-TRACER.md → ASSUMPTION-TRACER.md} +6 -10
- package/dist/templates/cc-native/_cc-native/agents/CLARITY-AUDITOR.md +6 -10
- package/dist/templates/cc-native/_cc-native/agents/CLAUDE.md +74 -3
- package/dist/templates/cc-native/_cc-native/agents/COMPLETENESS-FEASIBILITY.md +67 -0
- package/dist/templates/cc-native/_cc-native/agents/COMPLETENESS-GAPS.md +71 -0
- package/dist/templates/cc-native/_cc-native/agents/COMPLETENESS-ORDERING.md +63 -0
- package/dist/templates/cc-native/_cc-native/agents/CONSTRAINT-VALIDATOR.md +73 -0
- package/dist/templates/cc-native/_cc-native/agents/DESIGN-ADR-VALIDATOR.md +62 -0
- package/dist/templates/cc-native/_cc-native/agents/DESIGN-SCALE-MATCHER.md +65 -0
- package/dist/templates/cc-native/_cc-native/agents/DEVILS-ADVOCATE.md +6 -9
- package/dist/templates/cc-native/_cc-native/agents/DOCUMENTATION-PHILOSOPHY.md +87 -0
- package/dist/templates/cc-native/_cc-native/agents/HANDOFF-READINESS.md +5 -9
- package/dist/templates/cc-native/_cc-native/agents/{HIDDEN-COMPLEXITY-DETECTOR.md → HIDDEN-COMPLEXITY.md} +6 -10
- package/dist/templates/cc-native/_cc-native/agents/INCREMENTAL-DELIVERY.md +67 -0
- package/dist/templates/cc-native/_cc-native/agents/PLAN-ORCHESTRATOR.md +91 -18
- package/dist/templates/cc-native/_cc-native/agents/RISK-DEPENDENCY.md +63 -0
- package/dist/templates/cc-native/_cc-native/agents/RISK-FMEA.md +67 -0
- package/dist/templates/cc-native/_cc-native/agents/RISK-PREMORTEM.md +72 -0
- package/dist/templates/cc-native/_cc-native/agents/RISK-REVERSIBILITY.md +75 -0
- package/dist/templates/cc-native/_cc-native/agents/SCOPE-BOUNDARY.md +78 -0
- package/dist/templates/cc-native/_cc-native/agents/SIMPLICITY-GUARDIAN.md +5 -9
- package/dist/templates/cc-native/_cc-native/agents/SKEPTIC.md +16 -12
- package/dist/templates/cc-native/_cc-native/agents/TESTDRIVEN-BEHAVIOR-AUDITOR.md +62 -0
- package/dist/templates/cc-native/_cc-native/agents/TESTDRIVEN-CHARACTERIZATION.md +72 -0
- package/dist/templates/cc-native/_cc-native/agents/TESTDRIVEN-FIRST-VALIDATOR.md +62 -0
- package/dist/templates/cc-native/_cc-native/agents/TESTDRIVEN-PYRAMID-ANALYZER.md +62 -0
- package/dist/templates/cc-native/_cc-native/agents/TRADEOFF-COSTS.md +68 -0
- package/dist/templates/cc-native/_cc-native/agents/TRADEOFF-STAKEHOLDERS.md +66 -0
- package/dist/templates/cc-native/_cc-native/agents/VERIFY-COVERAGE.md +75 -0
- package/dist/templates/cc-native/_cc-native/agents/VERIFY-STRENGTH.md +70 -0
- package/dist/templates/cc-native/_cc-native/hooks/CLAUDE.md +109 -135
- package/dist/templates/cc-native/_cc-native/hooks/add_plan_context.ts +119 -0
- package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.ts +921 -0
- package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.ts +61 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/aggregate-agents.ts +157 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/artifacts.ts +709 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/cc-native-state.ts +199 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/cli-output-parser.ts +124 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/config.ts +57 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/constants.ts +83 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/debug.ts +80 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/index.ts +119 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/json-parser.ts +162 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/nul +3 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/orchestrator.ts +249 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/agent.ts +155 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/codex.ts +130 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/gemini.ts +106 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/index.ts +10 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/reviewers/types.ts +23 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/state.ts +243 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/tsconfig.json +18 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/types.ts +310 -0
- package/dist/templates/cc-native/_cc-native/lib-ts/verdict.ts +72 -0
- package/dist/templates/cc-native/_cc-native/plan-review.config.json +12 -16
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
- package/dist/lib/template-merger.d.ts +0 -47
- package/dist/lib/template-merger.js +0 -162
- package/dist/templates/_shared/hooks/__init__.py +0 -16
- package/dist/templates/_shared/hooks/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/archive_plan.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/context_enforcer.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/context_monitor.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/file-suggestion.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/pre_compact.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/session_end.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/session_start.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/task_create_atomicity.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/task_create_capture.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/task_update_capture.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/__pycache__/user_prompt_submit.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/archive_plan.py +0 -169
- package/dist/templates/_shared/hooks/context_monitor.py +0 -270
- package/dist/templates/_shared/hooks/file-suggestion.py +0 -215
- package/dist/templates/_shared/hooks/pre_compact.py +0 -104
- package/dist/templates/_shared/hooks/session_end.py +0 -173
- package/dist/templates/_shared/hooks/session_start.py +0 -206
- package/dist/templates/_shared/hooks/task_create_capture.py +0 -108
- package/dist/templates/_shared/hooks/task_update_capture.py +0 -145
- package/dist/templates/_shared/hooks/user_prompt_submit.py +0 -139
- package/dist/templates/_shared/lib/__init__.py +0 -1
- package/dist/templates/_shared/lib/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__init__.py +0 -65
- package/dist/templates/_shared/lib/base/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/atomic_write.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/constants.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/hook_utils.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/inference.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/logger.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/stop_words.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/subprocess_utils.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/__pycache__/utils.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/atomic_write.py +0 -180
- package/dist/templates/_shared/lib/base/constants.py +0 -358
- package/dist/templates/_shared/lib/base/hook_utils.py +0 -341
- package/dist/templates/_shared/lib/base/inference.py +0 -318
- package/dist/templates/_shared/lib/base/logger.py +0 -291
- package/dist/templates/_shared/lib/base/stop_words.py +0 -213
- package/dist/templates/_shared/lib/base/subprocess_utils.py +0 -46
- package/dist/templates/_shared/lib/base/utils.py +0 -242
- package/dist/templates/_shared/lib/context/__init__.py +0 -102
- 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_extractor.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/context_formatter.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__/context_selector.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/context_store.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/discovery.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/__pycache__/plan_archive.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/plan_manager.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/task_sync.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/__pycache__/task_tracker.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/context_formatter.py +0 -317
- package/dist/templates/_shared/lib/context/context_selector.py +0 -508
- package/dist/templates/_shared/lib/context/context_store.py +0 -653
- package/dist/templates/_shared/lib/context/plan_manager.py +0 -204
- package/dist/templates/_shared/lib/context/task_tracker.py +0 -188
- package/dist/templates/_shared/lib/handoff/__init__.py +0 -22
- package/dist/templates/_shared/lib/handoff/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/handoff/__pycache__/document_generator.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/handoff/document_generator.py +0 -278
- package/dist/templates/_shared/lib/templates/README.md +0 -206
- package/dist/templates/_shared/lib/templates/__init__.py +0 -36
- package/dist/templates/_shared/lib/templates/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/templates/__pycache__/formatters.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/templates/__pycache__/persona_questions.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/templates/__pycache__/plan_context.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/templates/formatters.py +0 -146
- package/dist/templates/_shared/scripts/__pycache__/save_handoff.cpython-313.pyc +0 -0
- package/dist/templates/_shared/scripts/__pycache__/status_line.cpython-313.pyc +0 -0
- package/dist/templates/_shared/scripts/save_handoff.py +0 -357
- package/dist/templates/_shared/scripts/status_line.py +0 -701
- package/dist/templates/cc-native/.claude/commands/cc-native/fresh-perspective.md +0 -8
- package/dist/templates/cc-native/.windsurf/workflows/cc-native/fresh-perspective.md +0 -8
- package/dist/templates/cc-native/MIGRATION.md +0 -86
- package/dist/templates/cc-native/_cc-native/agents/ACCESSIBILITY-TESTER.md +0 -79
- package/dist/templates/cc-native/_cc-native/agents/ARCHITECT-REVIEWER.md +0 -48
- package/dist/templates/cc-native/_cc-native/agents/CODE-REVIEWER.md +0 -70
- package/dist/templates/cc-native/_cc-native/agents/COMPLETENESS-CHECKER.md +0 -59
- package/dist/templates/cc-native/_cc-native/agents/CONTEXT-EXTRACTOR.md +0 -92
- package/dist/templates/cc-native/_cc-native/agents/DOCUMENTATION-REVIEWER.md +0 -51
- package/dist/templates/cc-native/_cc-native/agents/FEASIBILITY-ANALYST.md +0 -57
- package/dist/templates/cc-native/_cc-native/agents/FRESH-PERSPECTIVE.md +0 -54
- package/dist/templates/cc-native/_cc-native/agents/INCENTIVE-MAPPER.md +0 -61
- package/dist/templates/cc-native/_cc-native/agents/PENETRATION-TESTER.md +0 -79
- package/dist/templates/cc-native/_cc-native/agents/PERFORMANCE-ENGINEER.md +0 -75
- package/dist/templates/cc-native/_cc-native/agents/PRECEDENT-FINDER.md +0 -70
- package/dist/templates/cc-native/_cc-native/agents/REVERSIBILITY-ANALYST.md +0 -61
- package/dist/templates/cc-native/_cc-native/agents/RISK-ASSESSOR.md +0 -58
- package/dist/templates/cc-native/_cc-native/agents/SECOND-ORDER-ANALYST.md +0 -61
- package/dist/templates/cc-native/_cc-native/agents/STAKEHOLDER-ADVOCATE.md +0 -55
- package/dist/templates/cc-native/_cc-native/agents/TRADE-OFF-ILLUMINATOR.md +0 -204
- 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__/cc-native-plan-review.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/mark_questions_asked.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/plan_accepted.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/plan_questions_early.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/__pycache__/suggest-fresh-perspective.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/add_plan_context.py +0 -130
- package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.py +0 -869
- package/dist/templates/cc-native/_cc-native/hooks/plan_questions_early.py +0 -81
- package/dist/templates/cc-native/_cc-native/hooks/suggest-fresh-perspective.py +0 -340
- package/dist/templates/cc-native/_cc-native/lib/CLAUDE.md +0 -265
- package/dist/templates/cc-native/_cc-native/lib/__init__.py +0 -53
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/__init__.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/atomic_write.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/constants.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/debug.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/constants.py +0 -45
- package/dist/templates/cc-native/_cc-native/lib/debug.py +0 -139
- package/dist/templates/cc-native/_cc-native/lib/orchestrator.py +0 -362
- package/dist/templates/cc-native/_cc-native/lib/reviewers/__init__.py +0 -28
- 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 +0 -215
- package/dist/templates/cc-native/_cc-native/lib/reviewers/base.py +0 -88
- package/dist/templates/cc-native/_cc-native/lib/reviewers/codex.py +0 -124
- package/dist/templates/cc-native/_cc-native/lib/reviewers/gemini.py +0 -108
- package/dist/templates/cc-native/_cc-native/lib/state.py +0 -268
- package/dist/templates/cc-native/_cc-native/lib/utils.py +0 -1027
- 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 +0 -168
- package/dist/templates/cc-native/_cc-native/workflows/fresh-perspective.md +0 -134
package/dist/commands/clear.js
CHANGED
|
@@ -3,8 +3,9 @@ import { join } from 'node:path';
|
|
|
3
3
|
import { confirm } from '@inquirer/prompts';
|
|
4
4
|
import { Flags } from '@oclif/core';
|
|
5
5
|
import BaseCommand from '../lib/base-command.js';
|
|
6
|
-
import { pruneGitignoreStaleEntries } from '../lib/gitignore-manager.js';
|
|
6
|
+
import { computeGitignoreRemovals, pruneGitignoreStaleEntries, removeGitignoreEntries } from '../lib/gitignore-manager.js';
|
|
7
7
|
import { pathExists } from '../lib/paths.js';
|
|
8
|
+
import { reconstructIdeSettings } from '../lib/template-settings-reconstructor.js';
|
|
8
9
|
import { EXIT_CODES } from '../types/exit-codes.js';
|
|
9
10
|
/**
|
|
10
11
|
* Container folder for method-specific files
|
|
@@ -43,6 +44,7 @@ async function getInstalledMethodNames(targetDir) {
|
|
|
43
44
|
for (const ide of Object.values(IDE_FOLDERS)) {
|
|
44
45
|
const settingsPath = join(targetDir, ide.root, ide.settingsFile);
|
|
45
46
|
try {
|
|
47
|
+
// eslint-disable-next-line no-await-in-loop
|
|
46
48
|
const content = await fs.readFile(settingsPath, 'utf8');
|
|
47
49
|
const settings = JSON.parse(content);
|
|
48
50
|
if (settings.methods && typeof settings.methods === 'object') {
|
|
@@ -70,10 +72,6 @@ async function getInstalledMethodNames(targetDir) {
|
|
|
70
72
|
}
|
|
71
73
|
return methods;
|
|
72
74
|
}
|
|
73
|
-
/**
|
|
74
|
-
* AIW gitignore section header marker
|
|
75
|
-
*/
|
|
76
|
-
const AIW_GITIGNORE_HEADER = '# AIW Installation';
|
|
77
75
|
/**
|
|
78
76
|
* Check if a directory is empty.
|
|
79
77
|
*
|
|
@@ -189,217 +187,6 @@ async function shouldDeleteIdeFolder(targetDir, ideFolder) {
|
|
|
189
187
|
async function removeDirectory(dir) {
|
|
190
188
|
await fs.rm(dir, { force: true, recursive: true });
|
|
191
189
|
}
|
|
192
|
-
/**
|
|
193
|
-
* Update .gitignore to remove patterns for cleared folders.
|
|
194
|
-
* Removes patterns from the AIW Installation section.
|
|
195
|
-
*
|
|
196
|
-
* @param targetDir - Directory containing .gitignore
|
|
197
|
-
* @param foldersToRemove - Folder patterns to remove (without trailing slash)
|
|
198
|
-
*/
|
|
199
|
-
async function updateGitignoreAfterClear(targetDir, foldersToRemove) {
|
|
200
|
-
const gitignorePath = join(targetDir, '.gitignore');
|
|
201
|
-
try {
|
|
202
|
-
const content = await fs.readFile(gitignorePath, 'utf8');
|
|
203
|
-
// Check if AIW Installation section exists
|
|
204
|
-
if (!content.includes(AIW_GITIGNORE_HEADER)) {
|
|
205
|
-
return;
|
|
206
|
-
}
|
|
207
|
-
// Split content into lines
|
|
208
|
-
const lines = content.split('\n');
|
|
209
|
-
const newLines = [];
|
|
210
|
-
let inAiwSection = false;
|
|
211
|
-
const aiwSectionLines = [];
|
|
212
|
-
// Parse lines and identify AIW section
|
|
213
|
-
for (const line of lines) {
|
|
214
|
-
if (line === AIW_GITIGNORE_HEADER) {
|
|
215
|
-
inAiwSection = true;
|
|
216
|
-
aiwSectionLines.push(line);
|
|
217
|
-
continue;
|
|
218
|
-
}
|
|
219
|
-
if (inAiwSection) {
|
|
220
|
-
// AIW section ends at empty line or another comment header
|
|
221
|
-
if (line === '' || (line.startsWith('#') && line !== AIW_GITIGNORE_HEADER)) {
|
|
222
|
-
inAiwSection = false;
|
|
223
|
-
// Process AIW section lines now
|
|
224
|
-
const filteredAiwLines = filterAiwSection(aiwSectionLines, foldersToRemove);
|
|
225
|
-
newLines.push(...filteredAiwLines, line);
|
|
226
|
-
}
|
|
227
|
-
else {
|
|
228
|
-
aiwSectionLines.push(line);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
else {
|
|
232
|
-
newLines.push(line);
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
// Handle case where AIW section is at end of file
|
|
236
|
-
if (inAiwSection) {
|
|
237
|
-
const filteredAiwLines = filterAiwSection(aiwSectionLines, foldersToRemove);
|
|
238
|
-
newLines.push(...filteredAiwLines);
|
|
239
|
-
}
|
|
240
|
-
// Clean up: remove AIW section entirely if only header remains
|
|
241
|
-
const finalContent = cleanupGitignoreContent(newLines.join('\n'));
|
|
242
|
-
await fs.writeFile(gitignorePath, finalContent, 'utf8');
|
|
243
|
-
}
|
|
244
|
-
catch {
|
|
245
|
-
// .gitignore doesn't exist or can't be read
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
/**
|
|
249
|
-
* Filter AIW section lines to remove specified folders.
|
|
250
|
-
*
|
|
251
|
-
* @param aiwLines - Lines from AIW section (including header)
|
|
252
|
-
* @param foldersToRemove - Folder names to remove
|
|
253
|
-
* @returns Filtered lines
|
|
254
|
-
*/
|
|
255
|
-
function filterAiwSection(aiwLines, foldersToRemove) {
|
|
256
|
-
const patternsToRemove = new Set(foldersToRemove.map((f) => `${f}/`));
|
|
257
|
-
return aiwLines.filter((line) => {
|
|
258
|
-
// Always keep the header
|
|
259
|
-
if (line === AIW_GITIGNORE_HEADER) {
|
|
260
|
-
return true;
|
|
261
|
-
}
|
|
262
|
-
// Remove matching patterns
|
|
263
|
-
return !patternsToRemove.has(line);
|
|
264
|
-
});
|
|
265
|
-
}
|
|
266
|
-
/**
|
|
267
|
-
* Clean up gitignore content after filtering.
|
|
268
|
-
* Removes AIW section if empty, cleans up extra newlines.
|
|
269
|
-
*
|
|
270
|
-
* @param content - Gitignore content
|
|
271
|
-
* @returns Cleaned content
|
|
272
|
-
*/
|
|
273
|
-
function cleanupGitignoreContent(content) {
|
|
274
|
-
const lines = content.split('\n');
|
|
275
|
-
const newLines = [];
|
|
276
|
-
let i = 0;
|
|
277
|
-
while (i < lines.length) {
|
|
278
|
-
const line = lines[i];
|
|
279
|
-
// Check if this is an AIW header with nothing following
|
|
280
|
-
if (line === AIW_GITIGNORE_HEADER) {
|
|
281
|
-
// Look ahead to see if there are any patterns
|
|
282
|
-
let hasPatterns = false;
|
|
283
|
-
const nextLine = lines[i + 1];
|
|
284
|
-
if (nextLine !== undefined && nextLine !== '' && !nextLine.startsWith('#')) {
|
|
285
|
-
hasPatterns = true;
|
|
286
|
-
}
|
|
287
|
-
if (!hasPatterns) {
|
|
288
|
-
// Skip the AIW header since it has no patterns
|
|
289
|
-
i++;
|
|
290
|
-
// Also skip any trailing empty lines that were before AIW section
|
|
291
|
-
while (newLines.length > 0 && newLines.at(-1) === '') {
|
|
292
|
-
newLines.pop();
|
|
293
|
-
}
|
|
294
|
-
continue;
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
newLines.push(line);
|
|
298
|
-
i++;
|
|
299
|
-
}
|
|
300
|
-
// Ensure file ends with newline but not multiple
|
|
301
|
-
let result = newLines.join('\n');
|
|
302
|
-
result = result.replace(/\n+$/, '\n');
|
|
303
|
-
// Handle empty file case
|
|
304
|
-
if (result.trim() === '') {
|
|
305
|
-
return '';
|
|
306
|
-
}
|
|
307
|
-
return result;
|
|
308
|
-
}
|
|
309
|
-
/**
|
|
310
|
-
* Update IDE settings file to remove method-specific entries.
|
|
311
|
-
* Creates a backup before modifying.
|
|
312
|
-
*
|
|
313
|
-
* @param targetDir - Directory containing the IDE folder
|
|
314
|
-
* @param ideFolder - IDE folder configuration (e.g., IDE_FOLDERS.claude)
|
|
315
|
-
* @param ideFolder.root - Root folder name (e.g., '.claude')
|
|
316
|
-
* @param ideFolder.settingsFile - Settings file name (e.g., 'settings.json')
|
|
317
|
-
* @param methodsToRemove - Method names to remove from settings
|
|
318
|
-
*/
|
|
319
|
-
async function updateIdeSettings(targetDir, ideFolder, methodsToRemove) {
|
|
320
|
-
const settingsPath = join(targetDir, ideFolder.root, ideFolder.settingsFile);
|
|
321
|
-
const result = { backedUp: false, updated: false };
|
|
322
|
-
try {
|
|
323
|
-
const content = await fs.readFile(settingsPath, 'utf8');
|
|
324
|
-
const settings = JSON.parse(content);
|
|
325
|
-
// Create backup before modifying
|
|
326
|
-
const backupPath = `${settingsPath}.backup`;
|
|
327
|
-
await fs.writeFile(backupPath, content, 'utf8');
|
|
328
|
-
result.backedUp = true;
|
|
329
|
-
// Remove method-specific entries from methods tracking object
|
|
330
|
-
let modified = false;
|
|
331
|
-
if (settings.methods && typeof settings.methods === 'object') {
|
|
332
|
-
for (const method of methodsToRemove) {
|
|
333
|
-
if (method in settings.methods) {
|
|
334
|
-
delete settings.methods[method];
|
|
335
|
-
modified = true;
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
// Remove methods object if empty
|
|
339
|
-
if (Object.keys(settings.methods).length === 0) {
|
|
340
|
-
delete settings.methods;
|
|
341
|
-
modified = true;
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
// Remove method-specific hooks from hooks array
|
|
345
|
-
if (settings.hooks && typeof settings.hooks === 'object') {
|
|
346
|
-
for (const hookType of Object.keys(settings.hooks)) {
|
|
347
|
-
const hookArray = settings.hooks[hookType];
|
|
348
|
-
if (Array.isArray(hookArray)) {
|
|
349
|
-
const filtered = hookArray.filter((hook) => {
|
|
350
|
-
// Check if hook command references any of the methods to remove
|
|
351
|
-
if (hook.hooks && Array.isArray(hook.hooks)) {
|
|
352
|
-
const filteredInner = hook.hooks.filter((innerHook) => {
|
|
353
|
-
if (typeof innerHook.command === 'string') {
|
|
354
|
-
return !methodsToRemove.some((method) => innerHook.command?.toString().includes(`_${method}/`));
|
|
355
|
-
}
|
|
356
|
-
return true;
|
|
357
|
-
});
|
|
358
|
-
if (filteredInner.length !== hook.hooks.length) {
|
|
359
|
-
hook.hooks = filteredInner;
|
|
360
|
-
modified = true;
|
|
361
|
-
}
|
|
362
|
-
// Remove hook entry if all inner hooks were removed
|
|
363
|
-
if (filteredInner.length === 0) {
|
|
364
|
-
return false;
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
return true;
|
|
368
|
-
});
|
|
369
|
-
if (filtered.length !== hookArray.length) {
|
|
370
|
-
settings.hooks[hookType] = filtered;
|
|
371
|
-
modified = true;
|
|
372
|
-
}
|
|
373
|
-
// Remove hook type if empty
|
|
374
|
-
if (filtered.length === 0) {
|
|
375
|
-
delete settings.hooks[hookType];
|
|
376
|
-
modified = true;
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
// Remove hooks object if empty
|
|
381
|
-
if (Object.keys(settings.hooks).length === 0) {
|
|
382
|
-
delete settings.hooks;
|
|
383
|
-
modified = true;
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
if (modified) {
|
|
387
|
-
// Write updated settings
|
|
388
|
-
const newContent = JSON.stringify(settings, null, 2) + '\n';
|
|
389
|
-
await fs.writeFile(settingsPath, newContent, 'utf8');
|
|
390
|
-
result.updated = true;
|
|
391
|
-
}
|
|
392
|
-
else {
|
|
393
|
-
// No changes needed, remove backup
|
|
394
|
-
await fs.unlink(backupPath);
|
|
395
|
-
result.backedUp = false;
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
catch {
|
|
399
|
-
// Settings file doesn't exist or can't be read
|
|
400
|
-
}
|
|
401
|
-
return result;
|
|
402
|
-
}
|
|
403
190
|
/**
|
|
404
191
|
* Clear workflow folders, output folders, IDE method folders, and update configurations.
|
|
405
192
|
*/
|
|
@@ -591,6 +378,18 @@ export default class ClearCommand extends BaseCommand {
|
|
|
591
378
|
this.logInfo(`${IDE_FOLDERS.windsurf.root}/ folder will be removed (will be empty)`);
|
|
592
379
|
this.log('');
|
|
593
380
|
}
|
|
381
|
+
// Compute gitignore changes for dry-run display
|
|
382
|
+
const gitignoreSimulation = await computeGitignoreRemovals(targetDir);
|
|
383
|
+
if (gitignoreSimulation.toRemove.length > 0 || gitignoreSimulation.toKeep.length > 0) {
|
|
384
|
+
this.logInfo('Gitignore changes:');
|
|
385
|
+
for (const { entry, reason } of gitignoreSimulation.toKeep) {
|
|
386
|
+
this.log(` keep ${entry}/ (${reason})`);
|
|
387
|
+
}
|
|
388
|
+
for (const entry of gitignoreSimulation.toRemove) {
|
|
389
|
+
this.log(` remove ${entry}/`);
|
|
390
|
+
}
|
|
391
|
+
this.log('');
|
|
392
|
+
}
|
|
594
393
|
// Dry run - just show what would happen
|
|
595
394
|
if (flags['dry-run']) {
|
|
596
395
|
this.logInfo('Dry run complete. No files or folders were deleted.');
|
|
@@ -654,29 +453,48 @@ export default class ClearCommand extends BaseCommand {
|
|
|
654
453
|
catch {
|
|
655
454
|
// .aiwcli doesn't exist or can't be accessed
|
|
656
455
|
}
|
|
657
|
-
//
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
this.logDebug(
|
|
456
|
+
// Smart gitignore removal: compute what should be removed based on disk state
|
|
457
|
+
const { toRemove, toKeep } = await computeGitignoreRemovals(targetDir);
|
|
458
|
+
for (const { entry, reason } of toKeep) {
|
|
459
|
+
this.logDebug(`Keeping ${entry}/ in .gitignore (${reason})`);
|
|
460
|
+
}
|
|
461
|
+
if (toRemove.length > 0) {
|
|
462
|
+
await removeGitignoreEntries(targetDir, toRemove);
|
|
463
|
+
this.logDebug(`Removed from .gitignore: ${toRemove.join(', ')}`);
|
|
661
464
|
}
|
|
662
|
-
// Prune stale gitignore entries
|
|
465
|
+
// Prune stale gitignore entries as safety net
|
|
663
466
|
const pruned = await pruneGitignoreStaleEntries(targetDir);
|
|
664
467
|
if (pruned) {
|
|
665
468
|
this.logDebug('Pruned stale .gitignore entries');
|
|
666
469
|
}
|
|
667
|
-
//
|
|
470
|
+
// Reconstruct IDE settings from remaining templates
|
|
668
471
|
let updatedClaudeSettings = false;
|
|
669
472
|
let updatedWindsurfSettings = false;
|
|
670
473
|
if (methodsToRemove.length > 0) {
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
const
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
474
|
+
// Remove method entries from settings files first
|
|
475
|
+
await this.removeMethodEntries(targetDir, methodsToRemove);
|
|
476
|
+
// Get remaining installed methods
|
|
477
|
+
const allMethods = await getInstalledMethodNames(targetDir);
|
|
478
|
+
// Filter out methods being removed (in case disk scan still finds them)
|
|
479
|
+
const remainingTemplates = [...allMethods].filter(m => !methodsToRemove.includes(m));
|
|
480
|
+
// Determine which IDEs need reconstruction
|
|
481
|
+
const ides = [];
|
|
482
|
+
if (await pathExists(join(targetDir, IDE_FOLDERS.claude.root))) {
|
|
483
|
+
ides.push('claude');
|
|
484
|
+
}
|
|
485
|
+
if (await pathExists(join(targetDir, IDE_FOLDERS.windsurf.root))) {
|
|
486
|
+
ides.push('windsurf');
|
|
487
|
+
}
|
|
488
|
+
if (ides.length > 0) {
|
|
489
|
+
await reconstructIdeSettings(targetDir, remainingTemplates, ides);
|
|
490
|
+
if (ides.includes('claude')) {
|
|
491
|
+
this.logDebug('Reconstructed .claude/settings.json (backup created)');
|
|
492
|
+
updatedClaudeSettings = true;
|
|
493
|
+
}
|
|
494
|
+
if (ides.includes('windsurf')) {
|
|
495
|
+
this.logDebug('Reconstructed .windsurf/hooks.json (backup created)');
|
|
496
|
+
updatedWindsurfSettings = true;
|
|
497
|
+
}
|
|
680
498
|
}
|
|
681
499
|
}
|
|
682
500
|
// Check if IDE folders should be fully deleted (empty settings + empty subfolders)
|
|
@@ -733,7 +551,7 @@ export default class ClearCommand extends BaseCommand {
|
|
|
733
551
|
parts.push(`${IDE_FOLDERS.windsurf.root}/ folder`);
|
|
734
552
|
}
|
|
735
553
|
this.logSuccess(`Cleared: ${parts.join(', ')}.`);
|
|
736
|
-
if (
|
|
554
|
+
if (toRemove.length > 0 || pruned) {
|
|
737
555
|
this.logSuccess('Updated .gitignore.');
|
|
738
556
|
}
|
|
739
557
|
if (updatedClaudeSettings) {
|
|
@@ -755,151 +573,6 @@ export default class ClearCommand extends BaseCommand {
|
|
|
755
573
|
});
|
|
756
574
|
}
|
|
757
575
|
}
|
|
758
|
-
/**
|
|
759
|
-
* Extract method names from workflow folder names (e.g., _gsd -> gsd).
|
|
760
|
-
*
|
|
761
|
-
* @param workflowFolders - Array of workflow folder paths
|
|
762
|
-
* @returns Array of method names
|
|
763
|
-
*/
|
|
764
|
-
extractMethodNames(workflowFolders) {
|
|
765
|
-
const methods = [];
|
|
766
|
-
for (const folder of workflowFolders) {
|
|
767
|
-
const folderName = folder.split(/[/\\]/).pop() || '';
|
|
768
|
-
if (folderName.startsWith('_')) {
|
|
769
|
-
methods.push(folderName.slice(1));
|
|
770
|
-
}
|
|
771
|
-
}
|
|
772
|
-
return methods;
|
|
773
|
-
}
|
|
774
|
-
/**
|
|
775
|
-
* Find all IDE method folders by scanning subdirectories of each IDE root
|
|
776
|
-
* for children matching installed method names.
|
|
777
|
-
*
|
|
778
|
-
* For example, finds .claude/commands/cc-native/, .claude/skills/cc-native/,
|
|
779
|
-
* .windsurf/workflows/cc-native/ — without hardcoding which subdirectories exist.
|
|
780
|
-
*
|
|
781
|
-
* @param targetDir - Directory to search in
|
|
782
|
-
* @param template - Optional template/method name to filter by
|
|
783
|
-
* @returns Array of IDE method folder paths
|
|
784
|
-
*/
|
|
785
|
-
async findIdeMethodFolders(targetDir, template) {
|
|
786
|
-
// Build method set: from --template flag, or from installed methods
|
|
787
|
-
const methodNames = template ? new Set([template]) : await getInstalledMethodNames(targetDir);
|
|
788
|
-
if (methodNames.size === 0) {
|
|
789
|
-
return [];
|
|
790
|
-
}
|
|
791
|
-
// For each IDE root, scan all subdirectories for children matching method names
|
|
792
|
-
const ideRoots = Object.values(IDE_FOLDERS).map((ide) => join(targetDir, ide.root));
|
|
793
|
-
const ideResults = await Promise.all(ideRoots.map(async (ideRoot) => {
|
|
794
|
-
// Get all subdirectories of IDE root (e.g., .claude/commands/, .claude/skills/)
|
|
795
|
-
try {
|
|
796
|
-
const topEntries = await fs.readdir(ideRoot, { withFileTypes: true });
|
|
797
|
-
const subdirs = topEntries.filter((e) => e.isDirectory());
|
|
798
|
-
// For each subdirectory, check for method-named children
|
|
799
|
-
const subResults = await Promise.all(subdirs.map(async (subdir) => {
|
|
800
|
-
const subdirPath = join(ideRoot, subdir.name);
|
|
801
|
-
try {
|
|
802
|
-
const entries = await fs.readdir(subdirPath, { withFileTypes: true });
|
|
803
|
-
return entries
|
|
804
|
-
.filter((entry) => entry.isDirectory() && methodNames.has(entry.name))
|
|
805
|
-
.map((entry) => join(subdirPath, entry.name));
|
|
806
|
-
}
|
|
807
|
-
catch {
|
|
808
|
-
return [];
|
|
809
|
-
}
|
|
810
|
-
}));
|
|
811
|
-
return subResults.flat();
|
|
812
|
-
}
|
|
813
|
-
catch {
|
|
814
|
-
return [];
|
|
815
|
-
}
|
|
816
|
-
}));
|
|
817
|
-
return ideResults.flat();
|
|
818
|
-
}
|
|
819
|
-
/**
|
|
820
|
-
* Find all output folders in the target directory.
|
|
821
|
-
* Looks for .aiwcli/_output/{method}/ structure.
|
|
822
|
-
*
|
|
823
|
-
* @param targetDir - Directory to search in
|
|
824
|
-
* @param template - Optional template/method name to filter by (e.g., 'bmad', 'gsd')
|
|
825
|
-
* @returns Array of output folder paths
|
|
826
|
-
*/
|
|
827
|
-
async findOutputFolders(targetDir, template) {
|
|
828
|
-
const containerDir = join(targetDir, AIWCLI_CONTAINER);
|
|
829
|
-
const outputDir = join(containerDir, OUTPUT_FOLDER_NAME);
|
|
830
|
-
// Check if _output folder exists
|
|
831
|
-
try {
|
|
832
|
-
const stat = await fs.stat(outputDir);
|
|
833
|
-
if (!stat.isDirectory()) {
|
|
834
|
-
return [];
|
|
835
|
-
}
|
|
836
|
-
}
|
|
837
|
-
catch {
|
|
838
|
-
// _output folder doesn't exist
|
|
839
|
-
return [];
|
|
840
|
-
}
|
|
841
|
-
// If template specified, only look for that specific method folder
|
|
842
|
-
if (template) {
|
|
843
|
-
const methodPath = join(outputDir, template);
|
|
844
|
-
try {
|
|
845
|
-
const stat = await fs.stat(methodPath);
|
|
846
|
-
if (stat.isDirectory()) {
|
|
847
|
-
return [methodPath];
|
|
848
|
-
}
|
|
849
|
-
}
|
|
850
|
-
catch {
|
|
851
|
-
// Method folder doesn't exist
|
|
852
|
-
}
|
|
853
|
-
return [];
|
|
854
|
-
}
|
|
855
|
-
// No template filter - find all method folders within _output
|
|
856
|
-
const foundFolders = [];
|
|
857
|
-
try {
|
|
858
|
-
const entries = await fs.readdir(outputDir, { withFileTypes: true });
|
|
859
|
-
for (const entry of entries) {
|
|
860
|
-
if (entry.isDirectory()) {
|
|
861
|
-
foundFolders.push(join(outputDir, entry.name));
|
|
862
|
-
}
|
|
863
|
-
}
|
|
864
|
-
}
|
|
865
|
-
catch {
|
|
866
|
-
// Directory can't be read - return empty
|
|
867
|
-
}
|
|
868
|
-
return foundFolders;
|
|
869
|
-
}
|
|
870
|
-
/**
|
|
871
|
-
* Find all workflow folders in the target directory.
|
|
872
|
-
* Looks for .aiwcli/_{method}/ structure (e.g., .aiwcli/_gsd/, .aiwcli/_bmad/).
|
|
873
|
-
*
|
|
874
|
-
* @param targetDir - Directory to search in
|
|
875
|
-
* @param template - Optional template/method name to filter by (e.g., 'bmad', 'gsd')
|
|
876
|
-
* @returns Array of workflow folder paths
|
|
877
|
-
*/
|
|
878
|
-
async findWorkflowFolders(targetDir, template) {
|
|
879
|
-
const foundFolders = [];
|
|
880
|
-
const containerDir = join(targetDir, AIWCLI_CONTAINER);
|
|
881
|
-
try {
|
|
882
|
-
const entries = await fs.readdir(containerDir, { withFileTypes: true });
|
|
883
|
-
for (const entry of entries) {
|
|
884
|
-
// Look for directories starting with underscore (workflow folders)
|
|
885
|
-
if (entry.isDirectory() && entry.name.startsWith('_') && entry.name !== OUTPUT_FOLDER_NAME) {
|
|
886
|
-
// If template specified, only include matching folder
|
|
887
|
-
if (template) {
|
|
888
|
-
if (entry.name === `_${template}`) {
|
|
889
|
-
foundFolders.push(join(containerDir, entry.name));
|
|
890
|
-
}
|
|
891
|
-
}
|
|
892
|
-
else {
|
|
893
|
-
foundFolders.push(join(containerDir, entry.name));
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
}
|
|
898
|
-
catch {
|
|
899
|
-
// Directory can't be read - return empty
|
|
900
|
-
}
|
|
901
|
-
return foundFolders;
|
|
902
|
-
}
|
|
903
576
|
/**
|
|
904
577
|
* Clean runtime output artifacts from _output/ at project root.
|
|
905
578
|
* Handles temp files, cache files, log rotation, and archive cleanup.
|
|
@@ -907,6 +580,7 @@ export default class ClearCommand extends BaseCommand {
|
|
|
907
580
|
* @param targetDir - Project root directory
|
|
908
581
|
* @param flags - Command flags (dry-run, force)
|
|
909
582
|
*/
|
|
583
|
+
// eslint-disable-next-line complexity
|
|
910
584
|
async cleanRuntimeOutput(targetDir, flags) {
|
|
911
585
|
const outputDir = join(targetDir, '_output');
|
|
912
586
|
if (!(await pathExists(outputDir))) {
|
|
@@ -934,7 +608,7 @@ export default class ClearCommand extends BaseCommand {
|
|
|
934
608
|
// Log rotation: hook-log.jsonl > 1MB
|
|
935
609
|
if (entry.isFile() && entry.name === 'hook-log.jsonl') {
|
|
936
610
|
try {
|
|
937
|
-
const stat = await fs.stat(entryPath);
|
|
611
|
+
const stat = await fs.stat(entryPath); // eslint-disable-line no-await-in-loop
|
|
938
612
|
if (stat.size > 1_048_576) {
|
|
939
613
|
logAction = { path: entryPath, sizeBytes: stat.size };
|
|
940
614
|
}
|
|
@@ -948,7 +622,7 @@ export default class ClearCommand extends BaseCommand {
|
|
|
948
622
|
if (entry.isDirectory() && entry.name === 'contexts') {
|
|
949
623
|
const archivePath = join(entryPath, '_archive');
|
|
950
624
|
try {
|
|
951
|
-
const archiveEntries = await fs.readdir(archivePath);
|
|
625
|
+
const archiveEntries = await fs.readdir(archivePath); // eslint-disable-line no-await-in-loop
|
|
952
626
|
if (archiveEntries.length > 0) {
|
|
953
627
|
archiveDir = archivePath;
|
|
954
628
|
archiveCount = archiveEntries.length;
|
|
@@ -1008,7 +682,7 @@ export default class ClearCommand extends BaseCommand {
|
|
|
1008
682
|
let deletedCount = 0;
|
|
1009
683
|
for (const item of toDelete) {
|
|
1010
684
|
try {
|
|
1011
|
-
await fs.unlink(item.path);
|
|
685
|
+
await fs.unlink(item.path); // eslint-disable-line no-await-in-loop
|
|
1012
686
|
deletedCount++;
|
|
1013
687
|
}
|
|
1014
688
|
catch (error) {
|
|
@@ -1024,7 +698,7 @@ export default class ClearCommand extends BaseCommand {
|
|
|
1024
698
|
const truncated = content.slice(-524_288);
|
|
1025
699
|
// Find the first complete line
|
|
1026
700
|
const firstNewline = truncated.indexOf('\n');
|
|
1027
|
-
const cleaned = firstNewline
|
|
701
|
+
const cleaned = firstNewline === -1 ? truncated : truncated.slice(firstNewline + 1);
|
|
1028
702
|
await fs.writeFile(logAction.path, cleaned, 'utf8');
|
|
1029
703
|
this.logDebug('Rotated hook-log.jsonl');
|
|
1030
704
|
}
|
|
@@ -1072,4 +746,178 @@ export default class ClearCommand extends BaseCommand {
|
|
|
1072
746
|
this.logInfo('No changes made.');
|
|
1073
747
|
}
|
|
1074
748
|
}
|
|
749
|
+
/**
|
|
750
|
+
* Extract method names from workflow folder names (e.g., _gsd -> gsd).
|
|
751
|
+
*
|
|
752
|
+
* @param workflowFolders - Array of workflow folder paths
|
|
753
|
+
* @returns Array of method names
|
|
754
|
+
*/
|
|
755
|
+
extractMethodNames(workflowFolders) {
|
|
756
|
+
const methods = [];
|
|
757
|
+
for (const folder of workflowFolders) {
|
|
758
|
+
const folderName = folder.split(/[/\\]/).pop() || '';
|
|
759
|
+
if (folderName.startsWith('_')) {
|
|
760
|
+
methods.push(folderName.slice(1));
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
return methods;
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
766
|
+
* Find all IDE method folders by scanning subdirectories of each IDE root
|
|
767
|
+
* for children matching installed method names.
|
|
768
|
+
*
|
|
769
|
+
* For example, finds .claude/commands/cc-native/, .claude/skills/cc-native/,
|
|
770
|
+
* .windsurf/workflows/cc-native/ — without hardcoding which subdirectories exist.
|
|
771
|
+
*
|
|
772
|
+
* @param targetDir - Directory to search in
|
|
773
|
+
* @param template - Optional template/method name to filter by
|
|
774
|
+
* @returns Array of IDE method folder paths
|
|
775
|
+
*/
|
|
776
|
+
async findIdeMethodFolders(targetDir, template) {
|
|
777
|
+
// Build method set: from --template flag, or from installed methods
|
|
778
|
+
const methodNames = template ? new Set([template]) : await getInstalledMethodNames(targetDir);
|
|
779
|
+
if (methodNames.size === 0) {
|
|
780
|
+
return [];
|
|
781
|
+
}
|
|
782
|
+
// For each IDE root, scan all subdirectories for children matching method names
|
|
783
|
+
const ideRoots = Object.values(IDE_FOLDERS).map((ide) => join(targetDir, ide.root));
|
|
784
|
+
const ideResults = await Promise.all(ideRoots.map(async (ideRoot) => {
|
|
785
|
+
// Get all subdirectories of IDE root (e.g., .claude/commands/, .claude/skills/)
|
|
786
|
+
try {
|
|
787
|
+
const topEntries = await fs.readdir(ideRoot, { withFileTypes: true });
|
|
788
|
+
const subdirs = topEntries.filter((e) => e.isDirectory());
|
|
789
|
+
// For each subdirectory, check for method-named children
|
|
790
|
+
const subResults = await Promise.all(subdirs.map(async (subdir) => {
|
|
791
|
+
const subdirPath = join(ideRoot, subdir.name);
|
|
792
|
+
try {
|
|
793
|
+
const entries = await fs.readdir(subdirPath, { withFileTypes: true });
|
|
794
|
+
return entries
|
|
795
|
+
.filter((entry) => entry.isDirectory() && methodNames.has(entry.name))
|
|
796
|
+
.map((entry) => join(subdirPath, entry.name));
|
|
797
|
+
}
|
|
798
|
+
catch {
|
|
799
|
+
return [];
|
|
800
|
+
}
|
|
801
|
+
}));
|
|
802
|
+
return subResults.flat();
|
|
803
|
+
}
|
|
804
|
+
catch {
|
|
805
|
+
return [];
|
|
806
|
+
}
|
|
807
|
+
}));
|
|
808
|
+
return ideResults.flat();
|
|
809
|
+
}
|
|
810
|
+
/**
|
|
811
|
+
* Find all output folders in the target directory.
|
|
812
|
+
* Looks for .aiwcli/_output/{method}/ structure.
|
|
813
|
+
*
|
|
814
|
+
* @param targetDir - Directory to search in
|
|
815
|
+
* @param template - Optional template/method name to filter by (e.g., 'bmad', 'gsd')
|
|
816
|
+
* @returns Array of output folder paths
|
|
817
|
+
*/
|
|
818
|
+
async findOutputFolders(targetDir, template) {
|
|
819
|
+
const containerDir = join(targetDir, AIWCLI_CONTAINER);
|
|
820
|
+
const outputDir = join(containerDir, OUTPUT_FOLDER_NAME);
|
|
821
|
+
// Check if _output folder exists
|
|
822
|
+
try {
|
|
823
|
+
const stat = await fs.stat(outputDir);
|
|
824
|
+
if (!stat.isDirectory()) {
|
|
825
|
+
return [];
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
catch {
|
|
829
|
+
// _output folder doesn't exist
|
|
830
|
+
return [];
|
|
831
|
+
}
|
|
832
|
+
// If template specified, only look for that specific method folder
|
|
833
|
+
if (template) {
|
|
834
|
+
const methodPath = join(outputDir, template);
|
|
835
|
+
try {
|
|
836
|
+
const stat = await fs.stat(methodPath);
|
|
837
|
+
if (stat.isDirectory()) {
|
|
838
|
+
return [methodPath];
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
catch {
|
|
842
|
+
// Method folder doesn't exist
|
|
843
|
+
}
|
|
844
|
+
return [];
|
|
845
|
+
}
|
|
846
|
+
// No template filter - find all method folders within _output
|
|
847
|
+
const foundFolders = [];
|
|
848
|
+
try {
|
|
849
|
+
const entries = await fs.readdir(outputDir, { withFileTypes: true });
|
|
850
|
+
for (const entry of entries) {
|
|
851
|
+
if (entry.isDirectory()) {
|
|
852
|
+
foundFolders.push(join(outputDir, entry.name));
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
catch {
|
|
857
|
+
// Directory can't be read - return empty
|
|
858
|
+
}
|
|
859
|
+
return foundFolders;
|
|
860
|
+
}
|
|
861
|
+
/**
|
|
862
|
+
* Find all workflow folders in the target directory.
|
|
863
|
+
* Looks for .aiwcli/_{method}/ structure (e.g., .aiwcli/_gsd/, .aiwcli/_bmad/).
|
|
864
|
+
*
|
|
865
|
+
* @param targetDir - Directory to search in
|
|
866
|
+
* @param template - Optional template/method name to filter by (e.g., 'bmad', 'gsd')
|
|
867
|
+
* @returns Array of workflow folder paths
|
|
868
|
+
*/
|
|
869
|
+
async findWorkflowFolders(targetDir, template) {
|
|
870
|
+
const foundFolders = [];
|
|
871
|
+
const containerDir = join(targetDir, AIWCLI_CONTAINER);
|
|
872
|
+
try {
|
|
873
|
+
const entries = await fs.readdir(containerDir, { withFileTypes: true });
|
|
874
|
+
for (const entry of entries) {
|
|
875
|
+
// Look for directories starting with underscore (workflow folders)
|
|
876
|
+
if (entry.isDirectory() && entry.name.startsWith('_') && entry.name !== OUTPUT_FOLDER_NAME) {
|
|
877
|
+
// If template specified, only include matching folder
|
|
878
|
+
if (template) {
|
|
879
|
+
if (entry.name === `_${template}`) {
|
|
880
|
+
foundFolders.push(join(containerDir, entry.name));
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
else {
|
|
884
|
+
foundFolders.push(join(containerDir, entry.name));
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
catch {
|
|
890
|
+
// Directory can't be read - return empty
|
|
891
|
+
}
|
|
892
|
+
return foundFolders;
|
|
893
|
+
}
|
|
894
|
+
/**
|
|
895
|
+
* Remove method entries from IDE settings files (methods tracking only).
|
|
896
|
+
* Settings reconstruction handles hooks/permissions; this only strips the methods object.
|
|
897
|
+
*/
|
|
898
|
+
async removeMethodEntries(targetDir, methodsToRemove) {
|
|
899
|
+
const ops = Object.values(IDE_FOLDERS).map(async (ide) => {
|
|
900
|
+
const settingsPath = join(targetDir, ide.root, ide.settingsFile);
|
|
901
|
+
try {
|
|
902
|
+
const content = await fs.readFile(settingsPath, 'utf8');
|
|
903
|
+
const settings = JSON.parse(content);
|
|
904
|
+
if (settings.methods && typeof settings.methods === 'object') {
|
|
905
|
+
for (const method of methodsToRemove) {
|
|
906
|
+
if (method in settings.methods) {
|
|
907
|
+
delete settings.methods[method];
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
if (Object.keys(settings.methods).length === 0) {
|
|
911
|
+
delete settings.methods;
|
|
912
|
+
}
|
|
913
|
+
// Write back with methods removed (backup created by reconstructor)
|
|
914
|
+
await fs.writeFile(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf8');
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
catch {
|
|
918
|
+
// Settings file doesn't exist or can't be read
|
|
919
|
+
}
|
|
920
|
+
});
|
|
921
|
+
await Promise.all(ops);
|
|
922
|
+
}
|
|
1075
923
|
}
|