aiwcli 0.10.2 → 0.10.3
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/dist/commands/clear.d.ts +11 -6
- package/dist/commands/clear.js +200 -249
- package/dist/commands/init/index.d.ts +1 -17
- package/dist/commands/init/index.js +19 -104
- 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/_shared/hooks/__pycache__/archive_plan.cpython-313.pyc +0 -0
- package/dist/templates/_shared/hooks/archive_plan.py +10 -2
- 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__/utils.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/base/hook_utils.py +8 -10
- package/dist/templates/_shared/lib/base/inference.py +51 -62
- package/dist/templates/_shared/lib/base/logger.py +35 -21
- package/dist/templates/_shared/lib/base/stop_words.py +8 -0
- package/dist/templates/_shared/lib/base/utils.py +29 -8
- package/dist/templates/_shared/lib/context/__pycache__/plan_manager.cpython-313.pyc +0 -0
- package/dist/templates/_shared/lib/context/plan_manager.py +101 -2
- package/dist/templates/_shared/lib-ts/base/atomic-write.ts +138 -0
- package/dist/templates/_shared/lib-ts/base/constants.ts +299 -0
- package/dist/templates/_shared/lib-ts/base/git-state.ts +58 -0
- package/dist/templates/_shared/lib-ts/base/hook-utils.ts +360 -0
- package/dist/templates/_shared/lib-ts/base/inference.ts +245 -0
- package/dist/templates/_shared/lib-ts/base/logger.ts +234 -0
- package/dist/templates/_shared/lib-ts/base/state-io.ts +114 -0
- package/dist/templates/_shared/lib-ts/base/stop-words.ts +184 -0
- package/dist/templates/_shared/lib-ts/base/subprocess-utils.ts +23 -0
- package/dist/templates/_shared/lib-ts/base/utils.ts +184 -0
- package/dist/templates/_shared/lib-ts/context/context-formatter.ts +432 -0
- package/dist/templates/_shared/lib-ts/context/context-selector.ts +497 -0
- package/dist/templates/_shared/lib-ts/context/context-store.ts +679 -0
- package/dist/templates/_shared/lib-ts/context/plan-manager.ts +292 -0
- package/dist/templates/_shared/lib-ts/context/task-tracker.ts +181 -0
- package/dist/templates/_shared/lib-ts/handoff/document-generator.ts +215 -0
- package/dist/templates/_shared/lib-ts/package.json +21 -0
- package/dist/templates/_shared/lib-ts/templates/formatters.ts +102 -0
- package/dist/templates/_shared/lib-ts/templates/plan-context.ts +65 -0
- package/dist/templates/_shared/lib-ts/tsconfig.json +13 -0
- package/dist/templates/_shared/lib-ts/types.ts +151 -0
- package/dist/templates/_shared/scripts/__pycache__/status_line.cpython-313.pyc +0 -0
- package/dist/templates/_shared/scripts/save_handoff.ts +359 -0
- package/dist/templates/_shared/scripts/status_line.py +17 -2
- 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 -1
- 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/__pycache__/cc-native-plan-review.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/hooks/cc-native-plan-review.py +125 -40
- package/dist/templates/cc-native/_cc-native/lib/__pycache__/utils.cpython-313.pyc +0 -0
- package/dist/templates/cc-native/_cc-native/lib/utils.py +57 -13
- package/dist/templates/cc-native/_cc-native/plan-review.config.json +11 -7
- 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/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/commands/clear.d.ts
CHANGED
|
@@ -15,6 +15,14 @@ export default class ClearCommand extends BaseCommand {
|
|
|
15
15
|
quiet: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
16
16
|
};
|
|
17
17
|
run(): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* Clean runtime output artifacts from _output/ at project root.
|
|
20
|
+
* Handles temp files, cache files, log rotation, and archive cleanup.
|
|
21
|
+
*
|
|
22
|
+
* @param targetDir - Project root directory
|
|
23
|
+
* @param flags - Command flags (dry-run, force)
|
|
24
|
+
*/
|
|
25
|
+
private cleanRuntimeOutput;
|
|
18
26
|
/**
|
|
19
27
|
* Extract method names from workflow folder names (e.g., _gsd -> gsd).
|
|
20
28
|
*
|
|
@@ -53,11 +61,8 @@ export default class ClearCommand extends BaseCommand {
|
|
|
53
61
|
*/
|
|
54
62
|
private findWorkflowFolders;
|
|
55
63
|
/**
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
* @param targetDir - Project root directory
|
|
60
|
-
* @param flags - Command flags (dry-run, force)
|
|
64
|
+
* Remove method entries from IDE settings files (methods tracking only).
|
|
65
|
+
* Settings reconstruction handles hooks/permissions; this only strips the methods object.
|
|
61
66
|
*/
|
|
62
|
-
private
|
|
67
|
+
private removeMethodEntries;
|
|
63
68
|
}
|
package/dist/commands/clear.js
CHANGED
|
@@ -5,6 +5,7 @@ import { Flags } from '@oclif/core';
|
|
|
5
5
|
import BaseCommand from '../lib/base-command.js';
|
|
6
6
|
import { pruneGitignoreStaleEntries } 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
|
|
@@ -306,100 +307,6 @@ function cleanupGitignoreContent(content) {
|
|
|
306
307
|
}
|
|
307
308
|
return result;
|
|
308
309
|
}
|
|
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
310
|
/**
|
|
404
311
|
* Clear workflow folders, output folders, IDE method folders, and update configurations.
|
|
405
312
|
*/
|
|
@@ -664,19 +571,34 @@ export default class ClearCommand extends BaseCommand {
|
|
|
664
571
|
if (pruned) {
|
|
665
572
|
this.logDebug('Pruned stale .gitignore entries');
|
|
666
573
|
}
|
|
667
|
-
//
|
|
574
|
+
// Reconstruct IDE settings from remaining templates
|
|
668
575
|
let updatedClaudeSettings = false;
|
|
669
576
|
let updatedWindsurfSettings = false;
|
|
670
577
|
if (methodsToRemove.length > 0) {
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
578
|
+
// Remove method entries from settings files first
|
|
579
|
+
await this.removeMethodEntries(targetDir, methodsToRemove);
|
|
580
|
+
// Get remaining installed methods
|
|
581
|
+
const allMethods = await getInstalledMethodNames(targetDir);
|
|
582
|
+
// Filter out methods being removed (in case disk scan still finds them)
|
|
583
|
+
const remainingTemplates = [...allMethods].filter(m => !methodsToRemove.includes(m));
|
|
584
|
+
// Determine which IDEs need reconstruction
|
|
585
|
+
const ides = [];
|
|
586
|
+
if (await pathExists(join(targetDir, IDE_FOLDERS.claude.root))) {
|
|
587
|
+
ides.push('claude');
|
|
675
588
|
}
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
589
|
+
if (await pathExists(join(targetDir, IDE_FOLDERS.windsurf.root))) {
|
|
590
|
+
ides.push('windsurf');
|
|
591
|
+
}
|
|
592
|
+
if (ides.length > 0) {
|
|
593
|
+
await reconstructIdeSettings(targetDir, remainingTemplates, ides);
|
|
594
|
+
if (ides.includes('claude')) {
|
|
595
|
+
this.logDebug('Reconstructed .claude/settings.json (backup created)');
|
|
596
|
+
updatedClaudeSettings = true;
|
|
597
|
+
}
|
|
598
|
+
if (ides.includes('windsurf')) {
|
|
599
|
+
this.logDebug('Reconstructed .windsurf/hooks.json (backup created)');
|
|
600
|
+
updatedWindsurfSettings = true;
|
|
601
|
+
}
|
|
680
602
|
}
|
|
681
603
|
}
|
|
682
604
|
// Check if IDE folders should be fully deleted (empty settings + empty subfolders)
|
|
@@ -755,151 +677,6 @@ export default class ClearCommand extends BaseCommand {
|
|
|
755
677
|
});
|
|
756
678
|
}
|
|
757
679
|
}
|
|
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
680
|
/**
|
|
904
681
|
* Clean runtime output artifacts from _output/ at project root.
|
|
905
682
|
* Handles temp files, cache files, log rotation, and archive cleanup.
|
|
@@ -1024,7 +801,7 @@ export default class ClearCommand extends BaseCommand {
|
|
|
1024
801
|
const truncated = content.slice(-524_288);
|
|
1025
802
|
// Find the first complete line
|
|
1026
803
|
const firstNewline = truncated.indexOf('\n');
|
|
1027
|
-
const cleaned = firstNewline
|
|
804
|
+
const cleaned = firstNewline === -1 ? truncated : truncated.slice(firstNewline + 1);
|
|
1028
805
|
await fs.writeFile(logAction.path, cleaned, 'utf8');
|
|
1029
806
|
this.logDebug('Rotated hook-log.jsonl');
|
|
1030
807
|
}
|
|
@@ -1072,4 +849,178 @@ export default class ClearCommand extends BaseCommand {
|
|
|
1072
849
|
this.logInfo('No changes made.');
|
|
1073
850
|
}
|
|
1074
851
|
}
|
|
852
|
+
/**
|
|
853
|
+
* Extract method names from workflow folder names (e.g., _gsd -> gsd).
|
|
854
|
+
*
|
|
855
|
+
* @param workflowFolders - Array of workflow folder paths
|
|
856
|
+
* @returns Array of method names
|
|
857
|
+
*/
|
|
858
|
+
extractMethodNames(workflowFolders) {
|
|
859
|
+
const methods = [];
|
|
860
|
+
for (const folder of workflowFolders) {
|
|
861
|
+
const folderName = folder.split(/[/\\]/).pop() || '';
|
|
862
|
+
if (folderName.startsWith('_')) {
|
|
863
|
+
methods.push(folderName.slice(1));
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
return methods;
|
|
867
|
+
}
|
|
868
|
+
/**
|
|
869
|
+
* Find all IDE method folders by scanning subdirectories of each IDE root
|
|
870
|
+
* for children matching installed method names.
|
|
871
|
+
*
|
|
872
|
+
* For example, finds .claude/commands/cc-native/, .claude/skills/cc-native/,
|
|
873
|
+
* .windsurf/workflows/cc-native/ — without hardcoding which subdirectories exist.
|
|
874
|
+
*
|
|
875
|
+
* @param targetDir - Directory to search in
|
|
876
|
+
* @param template - Optional template/method name to filter by
|
|
877
|
+
* @returns Array of IDE method folder paths
|
|
878
|
+
*/
|
|
879
|
+
async findIdeMethodFolders(targetDir, template) {
|
|
880
|
+
// Build method set: from --template flag, or from installed methods
|
|
881
|
+
const methodNames = template ? new Set([template]) : await getInstalledMethodNames(targetDir);
|
|
882
|
+
if (methodNames.size === 0) {
|
|
883
|
+
return [];
|
|
884
|
+
}
|
|
885
|
+
// For each IDE root, scan all subdirectories for children matching method names
|
|
886
|
+
const ideRoots = Object.values(IDE_FOLDERS).map((ide) => join(targetDir, ide.root));
|
|
887
|
+
const ideResults = await Promise.all(ideRoots.map(async (ideRoot) => {
|
|
888
|
+
// Get all subdirectories of IDE root (e.g., .claude/commands/, .claude/skills/)
|
|
889
|
+
try {
|
|
890
|
+
const topEntries = await fs.readdir(ideRoot, { withFileTypes: true });
|
|
891
|
+
const subdirs = topEntries.filter((e) => e.isDirectory());
|
|
892
|
+
// For each subdirectory, check for method-named children
|
|
893
|
+
const subResults = await Promise.all(subdirs.map(async (subdir) => {
|
|
894
|
+
const subdirPath = join(ideRoot, subdir.name);
|
|
895
|
+
try {
|
|
896
|
+
const entries = await fs.readdir(subdirPath, { withFileTypes: true });
|
|
897
|
+
return entries
|
|
898
|
+
.filter((entry) => entry.isDirectory() && methodNames.has(entry.name))
|
|
899
|
+
.map((entry) => join(subdirPath, entry.name));
|
|
900
|
+
}
|
|
901
|
+
catch {
|
|
902
|
+
return [];
|
|
903
|
+
}
|
|
904
|
+
}));
|
|
905
|
+
return subResults.flat();
|
|
906
|
+
}
|
|
907
|
+
catch {
|
|
908
|
+
return [];
|
|
909
|
+
}
|
|
910
|
+
}));
|
|
911
|
+
return ideResults.flat();
|
|
912
|
+
}
|
|
913
|
+
/**
|
|
914
|
+
* Find all output folders in the target directory.
|
|
915
|
+
* Looks for .aiwcli/_output/{method}/ structure.
|
|
916
|
+
*
|
|
917
|
+
* @param targetDir - Directory to search in
|
|
918
|
+
* @param template - Optional template/method name to filter by (e.g., 'bmad', 'gsd')
|
|
919
|
+
* @returns Array of output folder paths
|
|
920
|
+
*/
|
|
921
|
+
async findOutputFolders(targetDir, template) {
|
|
922
|
+
const containerDir = join(targetDir, AIWCLI_CONTAINER);
|
|
923
|
+
const outputDir = join(containerDir, OUTPUT_FOLDER_NAME);
|
|
924
|
+
// Check if _output folder exists
|
|
925
|
+
try {
|
|
926
|
+
const stat = await fs.stat(outputDir);
|
|
927
|
+
if (!stat.isDirectory()) {
|
|
928
|
+
return [];
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
catch {
|
|
932
|
+
// _output folder doesn't exist
|
|
933
|
+
return [];
|
|
934
|
+
}
|
|
935
|
+
// If template specified, only look for that specific method folder
|
|
936
|
+
if (template) {
|
|
937
|
+
const methodPath = join(outputDir, template);
|
|
938
|
+
try {
|
|
939
|
+
const stat = await fs.stat(methodPath);
|
|
940
|
+
if (stat.isDirectory()) {
|
|
941
|
+
return [methodPath];
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
catch {
|
|
945
|
+
// Method folder doesn't exist
|
|
946
|
+
}
|
|
947
|
+
return [];
|
|
948
|
+
}
|
|
949
|
+
// No template filter - find all method folders within _output
|
|
950
|
+
const foundFolders = [];
|
|
951
|
+
try {
|
|
952
|
+
const entries = await fs.readdir(outputDir, { withFileTypes: true });
|
|
953
|
+
for (const entry of entries) {
|
|
954
|
+
if (entry.isDirectory()) {
|
|
955
|
+
foundFolders.push(join(outputDir, entry.name));
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
catch {
|
|
960
|
+
// Directory can't be read - return empty
|
|
961
|
+
}
|
|
962
|
+
return foundFolders;
|
|
963
|
+
}
|
|
964
|
+
/**
|
|
965
|
+
* Find all workflow folders in the target directory.
|
|
966
|
+
* Looks for .aiwcli/_{method}/ structure (e.g., .aiwcli/_gsd/, .aiwcli/_bmad/).
|
|
967
|
+
*
|
|
968
|
+
* @param targetDir - Directory to search in
|
|
969
|
+
* @param template - Optional template/method name to filter by (e.g., 'bmad', 'gsd')
|
|
970
|
+
* @returns Array of workflow folder paths
|
|
971
|
+
*/
|
|
972
|
+
async findWorkflowFolders(targetDir, template) {
|
|
973
|
+
const foundFolders = [];
|
|
974
|
+
const containerDir = join(targetDir, AIWCLI_CONTAINER);
|
|
975
|
+
try {
|
|
976
|
+
const entries = await fs.readdir(containerDir, { withFileTypes: true });
|
|
977
|
+
for (const entry of entries) {
|
|
978
|
+
// Look for directories starting with underscore (workflow folders)
|
|
979
|
+
if (entry.isDirectory() && entry.name.startsWith('_') && entry.name !== OUTPUT_FOLDER_NAME) {
|
|
980
|
+
// If template specified, only include matching folder
|
|
981
|
+
if (template) {
|
|
982
|
+
if (entry.name === `_${template}`) {
|
|
983
|
+
foundFolders.push(join(containerDir, entry.name));
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
else {
|
|
987
|
+
foundFolders.push(join(containerDir, entry.name));
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
catch {
|
|
993
|
+
// Directory can't be read - return empty
|
|
994
|
+
}
|
|
995
|
+
return foundFolders;
|
|
996
|
+
}
|
|
997
|
+
/**
|
|
998
|
+
* Remove method entries from IDE settings files (methods tracking only).
|
|
999
|
+
* Settings reconstruction handles hooks/permissions; this only strips the methods object.
|
|
1000
|
+
*/
|
|
1001
|
+
async removeMethodEntries(targetDir, methodsToRemove) {
|
|
1002
|
+
const ops = Object.values(IDE_FOLDERS).map(async (ide) => {
|
|
1003
|
+
const settingsPath = join(targetDir, ide.root, ide.settingsFile);
|
|
1004
|
+
try {
|
|
1005
|
+
const content = await fs.readFile(settingsPath, 'utf8');
|
|
1006
|
+
const settings = JSON.parse(content);
|
|
1007
|
+
if (settings.methods && typeof settings.methods === 'object') {
|
|
1008
|
+
for (const method of methodsToRemove) {
|
|
1009
|
+
if (method in settings.methods) {
|
|
1010
|
+
delete settings.methods[method];
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
if (Object.keys(settings.methods).length === 0) {
|
|
1014
|
+
delete settings.methods;
|
|
1015
|
+
}
|
|
1016
|
+
// Write back with methods removed (backup created by reconstructor)
|
|
1017
|
+
await fs.writeFile(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf8');
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
catch {
|
|
1021
|
+
// Settings file doesn't exist or can't be read
|
|
1022
|
+
}
|
|
1023
|
+
});
|
|
1024
|
+
await Promise.all(ops);
|
|
1025
|
+
}
|
|
1075
1026
|
}
|
|
@@ -29,28 +29,12 @@ export default class Init extends BaseCommand {
|
|
|
29
29
|
* @returns Template description
|
|
30
30
|
*/
|
|
31
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
32
|
/**
|
|
49
33
|
* Perform post-installation actions.
|
|
50
34
|
*
|
|
51
35
|
* Handles:
|
|
52
36
|
* - Method tracking in settings.json
|
|
53
|
-
* - Settings
|
|
37
|
+
* - Settings reconstruction from all active templates
|
|
54
38
|
* - .gitignore updates
|
|
55
39
|
*
|
|
56
40
|
* @param config - Post-install configuration
|