@codyswann/lisa 1.0.0 → 1.0.5
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 +244 -36
- package/all/copy-overwrite/.claude/README.md +1 -3
- package/all/copy-overwrite/.claude/REFERENCE.md +519 -0
- package/all/copy-overwrite/.claude/agents/skill-evaluator.md +7 -7
- package/all/copy-overwrite/.claude/agents/test-coverage-agent.md +17 -0
- package/all/copy-overwrite/.claude/commands/git/commit.md +9 -5
- package/all/copy-overwrite/.claude/commands/git/submit-pr.md +1 -1
- package/all/copy-overwrite/.claude/commands/lisa/review-implementation.md +209 -0
- package/all/copy-overwrite/.claude/commands/project/add-test-coverage.md +58 -0
- package/all/copy-overwrite/.claude/commands/project/archive.md +1 -1
- package/all/copy-overwrite/.claude/commands/project/complete-task.md +53 -1
- package/all/copy-overwrite/.claude/commands/project/debrief.md +12 -23
- package/all/copy-overwrite/.claude/commands/project/execute.md +33 -77
- package/all/copy-overwrite/.claude/commands/project/fix-linter-error.md +87 -0
- package/all/copy-overwrite/.claude/commands/project/implement.md +24 -28
- package/all/copy-overwrite/.claude/commands/project/lower-code-complexity.md +30 -55
- package/all/copy-overwrite/.claude/commands/project/plan.md +87 -242
- package/all/copy-overwrite/.claude/commands/project/reduce-max-lines-per-function.md +76 -0
- package/all/copy-overwrite/.claude/commands/project/reduce-max-lines.md +75 -0
- package/all/copy-overwrite/.claude/commands/project/research.md +86 -188
- package/all/copy-overwrite/.claude/commands/project/review.md +19 -38
- package/all/copy-overwrite/.claude/commands/project/setup.md +1 -1
- package/all/copy-overwrite/.claude/commands/project/verify.md +62 -25
- package/all/copy-overwrite/.claude/commands/pull-request/review.md +25 -7
- package/all/copy-overwrite/.claude/commands/tasks/load.md +63 -0
- package/all/copy-overwrite/.claude/commands/tasks/sync.md +84 -0
- package/all/copy-overwrite/.claude/hooks/README.md +75 -0
- package/all/copy-overwrite/.claude/hooks/check-tired-boss.sh +61 -0
- package/all/copy-overwrite/.claude/hooks/debug-hook.sh +47 -0
- package/all/copy-overwrite/.claude/hooks/notify-ntfy.sh +2 -0
- package/all/copy-overwrite/.claude/hooks/sync-tasks.sh +95 -0
- package/all/copy-overwrite/.claude/{skills/coding-philosophy/SKILL.md → rules/coding-philosophy.md} +93 -70
- package/all/copy-overwrite/.claude/settings.json +35 -14
- package/all/copy-overwrite/.claude/skills/prompt-complexity-scorer/SKILL.md +41 -9
- package/all/copy-overwrite/.claude/skills/skill-creator/scripts/init_skill.py +2 -0
- package/all/copy-overwrite/.claude/skills/skill-creator/scripts/package_skill.py +2 -0
- package/all/copy-overwrite/.claude/skills/skill-creator/scripts/quick_validate.py +2 -0
- package/all/copy-overwrite/.safety-net.json +25 -0
- package/all/copy-overwrite/CLAUDE.md +8 -30
- package/all/copy-overwrite/HUMAN.md +517 -17
- package/all/create-only/.claude/rules/PROJECT_RULES.md +9 -0
- package/all/create-only/scripts/setup-deploy-key.sh +190 -0
- package/all/deletions.json +5 -0
- package/cdk/copy-overwrite/.github/workflows/ci.yml +142 -0
- package/cdk/copy-overwrite/.github/workflows/deploy.yml +59 -0
- package/cdk/copy-overwrite/eslint.cdk.ts +175 -0
- package/cdk/copy-overwrite/eslint.config.ts +51 -0
- package/cdk/copy-overwrite/eslint.slow.config.ts +80 -0
- package/cdk/copy-overwrite/knip.json +53 -0
- package/cdk/copy-overwrite/tsconfig.eslint.json +11 -0
- package/cdk/merge/package.json +17 -1
- package/dist/cli/index.d.ts +3 -2
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +83 -64
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/prompts.d.ts +17 -3
- package/dist/cli/prompts.d.ts.map +1 -1
- package/dist/cli/prompts.js +52 -16
- package/dist/cli/prompts.js.map +1 -1
- package/dist/core/config.d.ts +13 -4
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +17 -9
- package/dist/core/config.js.map +1 -1
- package/dist/core/git-service.d.ts +40 -0
- package/dist/core/git-service.d.ts.map +1 -0
- package/dist/core/git-service.js +52 -0
- package/dist/core/git-service.js.map +1 -0
- package/dist/core/index.d.ts +3 -3
- package/dist/core/index.js +3 -3
- package/dist/core/lisa.d.ts +124 -7
- package/dist/core/lisa.d.ts.map +1 -1
- package/dist/core/lisa.js +423 -221
- package/dist/core/lisa.js.map +1 -1
- package/dist/core/manifest.d.ts +5 -1
- package/dist/core/manifest.d.ts.map +1 -1
- package/dist/core/manifest.js +22 -16
- package/dist/core/manifest.js.map +1 -1
- package/dist/detection/detector.interface.d.ts +1 -1
- package/dist/detection/detectors/cdk.d.ts +6 -1
- package/dist/detection/detectors/cdk.d.ts.map +1 -1
- package/dist/detection/detectors/cdk.js +16 -8
- package/dist/detection/detectors/cdk.js.map +1 -1
- package/dist/detection/detectors/expo.d.ts +6 -1
- package/dist/detection/detectors/expo.d.ts.map +1 -1
- package/dist/detection/detectors/expo.js +13 -8
- package/dist/detection/detectors/expo.js.map +1 -1
- package/dist/detection/detectors/nestjs.d.ts +7 -2
- package/dist/detection/detectors/nestjs.d.ts.map +1 -1
- package/dist/detection/detectors/nestjs.js +17 -9
- package/dist/detection/detectors/nestjs.js.map +1 -1
- package/dist/detection/detectors/npm-package.d.ts +6 -1
- package/dist/detection/detectors/npm-package.d.ts.map +1 -1
- package/dist/detection/detectors/npm-package.js +9 -4
- package/dist/detection/detectors/npm-package.js.map +1 -1
- package/dist/detection/detectors/typescript.d.ts +6 -1
- package/dist/detection/detectors/typescript.d.ts.map +1 -1
- package/dist/detection/detectors/typescript.js +12 -7
- package/dist/detection/detectors/typescript.js.map +1 -1
- package/dist/detection/index.d.ts +13 -3
- package/dist/detection/index.d.ts.map +1 -1
- package/dist/detection/index.js +17 -7
- package/dist/detection/index.js.map +1 -1
- package/dist/errors/index.d.ts +66 -2
- package/dist/errors/index.d.ts.map +1 -1
- package/dist/errors/index.js +89 -17
- package/dist/errors/index.js.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/logging/console-logger.d.ts +21 -1
- package/dist/logging/console-logger.d.ts.map +1 -1
- package/dist/logging/console-logger.js +26 -6
- package/dist/logging/console-logger.js.map +1 -1
- package/dist/logging/index.d.ts +3 -3
- package/dist/logging/index.js +2 -2
- package/dist/logging/logger.interface.d.ts +1 -1
- package/dist/logging/silent-logger.d.ts +21 -1
- package/dist/logging/silent-logger.d.ts.map +1 -1
- package/dist/logging/silent-logger.js +20 -0
- package/dist/logging/silent-logger.js.map +1 -1
- package/dist/strategies/copy-contents.d.ts +47 -6
- package/dist/strategies/copy-contents.d.ts.map +1 -1
- package/dist/strategies/copy-contents.js +99 -49
- package/dist/strategies/copy-contents.js.map +1 -1
- package/dist/strategies/copy-overwrite.d.ts +10 -2
- package/dist/strategies/copy-overwrite.d.ts.map +1 -1
- package/dist/strategies/copy-overwrite.js +17 -9
- package/dist/strategies/copy-overwrite.js.map +1 -1
- package/dist/strategies/create-only.d.ts +10 -2
- package/dist/strategies/create-only.d.ts.map +1 -1
- package/dist/strategies/create-only.js +14 -6
- package/dist/strategies/create-only.js.map +1 -1
- package/dist/strategies/index.d.ts +17 -7
- package/dist/strategies/index.d.ts.map +1 -1
- package/dist/strategies/index.js +19 -9
- package/dist/strategies/index.js.map +1 -1
- package/dist/strategies/merge.d.ts +10 -2
- package/dist/strategies/merge.d.ts.map +1 -1
- package/dist/strategies/merge.js +21 -21
- package/dist/strategies/merge.js.map +1 -1
- package/dist/strategies/strategy.interface.d.ts +1 -1
- package/dist/strategies/strategy.interface.d.ts.map +1 -1
- package/dist/transaction/backup.d.ts +15 -1
- package/dist/transaction/backup.d.ts.map +1 -1
- package/dist/transaction/backup.js +47 -12
- package/dist/transaction/backup.js.map +1 -1
- package/dist/transaction/index.d.ts +3 -3
- package/dist/transaction/index.js +2 -2
- package/dist/transaction/transaction.d.ts +25 -2
- package/dist/transaction/transaction.d.ts.map +1 -1
- package/dist/transaction/transaction.js +25 -2
- package/dist/transaction/transaction.js.map +1 -1
- package/dist/utils/file-operations.d.ts +21 -0
- package/dist/utils/file-operations.d.ts.map +1 -1
- package/dist/utils/file-operations.js +48 -12
- package/dist/utils/file-operations.js.map +1 -1
- package/dist/utils/index.d.ts +3 -3
- package/dist/utils/index.js +3 -3
- package/dist/utils/json-utils.d.ts +12 -0
- package/dist/utils/json-utils.d.ts.map +1 -1
- package/dist/utils/json-utils.js +17 -5
- package/dist/utils/json-utils.js.map +1 -1
- package/dist/utils/path-utils.d.ts +11 -0
- package/dist/utils/path-utils.d.ts.map +1 -1
- package/dist/utils/path-utils.js +12 -1
- package/dist/utils/path-utils.js.map +1 -1
- package/eslint-plugin-code-organization/__tests__/enforce-statement-order.test.js +5 -0
- package/eslint-plugin-code-organization/index.js +5 -0
- package/eslint-plugin-code-organization/rules/enforce-statement-order.js +5 -0
- package/expo/copy-overwrite/.claude/skills/atomic-design-gluestack/scripts/validate_atomic_structure.py +2 -0
- package/expo/copy-overwrite/.claude/skills/container-view-pattern/scripts/create_component.py +2 -0
- package/expo/copy-overwrite/.claude/skills/container-view-pattern/scripts/validate_component.py +2 -0
- package/expo/copy-overwrite/.claude/skills/cross-platform-compatibility/scripts/validate_cross_platform.py +2 -0
- package/expo/copy-overwrite/.claude/skills/directory-structure/scripts/validate_structure.py +2 -0
- package/expo/copy-overwrite/.claude/skills/expo-router-best-practices/scripts/generate-route.py +2 -0
- package/expo/copy-overwrite/.claude/skills/gluestack-nativewind/scripts/validate_styling.py +2 -41
- package/{typescript → expo}/copy-overwrite/.github/workflows/build.yml +3 -0
- package/expo/copy-overwrite/.github/workflows/ci.yml +36 -0
- package/{typescript → expo}/copy-overwrite/.github/workflows/deploy.yml +22 -26
- package/{typescript → expo}/copy-overwrite/.github/workflows/lighthouse.yml +4 -1
- package/expo/copy-overwrite/eslint-plugin-component-structure/__tests__/plugin-index.test.js +5 -0
- package/expo/copy-overwrite/eslint-plugin-component-structure/__tests__/require-memo-in-view.test.js +5 -0
- package/expo/copy-overwrite/eslint-plugin-component-structure/__tests__/single-component-per-file.test.js +5 -0
- package/expo/copy-overwrite/eslint-plugin-component-structure/index.js +5 -0
- package/expo/copy-overwrite/eslint-plugin-component-structure/rules/enforce-component-structure.js +5 -0
- package/expo/copy-overwrite/eslint-plugin-component-structure/rules/no-return-in-view.js +6 -1
- package/expo/copy-overwrite/eslint-plugin-component-structure/rules/require-memo-in-view.js +5 -0
- package/expo/copy-overwrite/eslint-plugin-component-structure/rules/single-component-per-file.js +5 -0
- package/expo/copy-overwrite/eslint-plugin-ui-standards/README.md +0 -68
- package/expo/copy-overwrite/eslint-plugin-ui-standards/index.js +5 -3
- package/expo/copy-overwrite/eslint-plugin-ui-standards/rules/no-classname-outside-ui.js +5 -0
- package/expo/copy-overwrite/eslint-plugin-ui-standards/rules/no-direct-rn-imports.js +5 -0
- package/expo/copy-overwrite/eslint.config.ts +53 -0
- package/expo/copy-overwrite/eslint.expo.ts +330 -0
- package/expo/copy-overwrite/eslint.slow.config.ts +86 -0
- package/expo/copy-overwrite/knip.json +132 -0
- package/expo/copy-overwrite/lighthouserc.js +27 -0
- package/expo/copy-overwrite/tsconfig.eslint.json +25 -0
- package/expo/create-only/lighthouserc-config.json +6 -1
- package/expo/merge/package.json +16 -3
- package/nestjs/copy-overwrite/.claude/skills/nestjs-rules/SKILL.md +1 -1
- package/{typescript → nestjs}/copy-overwrite/.github/k6/README.md +2 -2
- package/{typescript → nestjs}/copy-overwrite/.github/k6/examples/customer-deploy-integration.yml +3 -0
- package/{typescript → nestjs}/copy-overwrite/.github/k6/examples/data-driven-test.js +5 -0
- package/{typescript → nestjs}/copy-overwrite/.github/k6/scenarios/load.js +6 -2
- package/{typescript → nestjs}/copy-overwrite/.github/k6/scenarios/smoke.js +5 -0
- package/{typescript → nestjs}/copy-overwrite/.github/k6/scenarios/soak.js +5 -0
- package/{typescript → nestjs}/copy-overwrite/.github/k6/scenarios/spike.js +5 -0
- package/{typescript → nestjs}/copy-overwrite/.github/k6/scenarios/stress.js +5 -0
- package/{typescript → nestjs}/copy-overwrite/.github/k6/scripts/api-test.js +5 -0
- package/{typescript → nestjs}/copy-overwrite/.github/k6/scripts/default-test.js +5 -0
- package/nestjs/copy-overwrite/.github/workflows/ci.yml +29 -0
- package/nestjs/copy-overwrite/.github/workflows/deploy.yml +291 -0
- package/{typescript → nestjs}/copy-overwrite/.github/workflows/load-test.yml +3 -0
- package/nestjs/copy-overwrite/eslint.config.ts +53 -0
- package/nestjs/copy-overwrite/eslint.nestjs.ts +178 -0
- package/nestjs/merge/package.json +11 -3
- package/package.json +34 -40
- package/typescript/copy-contents/.husky/pre-commit +1 -1
- package/typescript/copy-contents/.husky/pre-push +99 -118
- package/typescript/copy-overwrite/.claude/hooks/format-on-edit.sh +2 -0
- package/typescript/copy-overwrite/.claude/hooks/install_pkgs.sh +3 -11
- package/typescript/copy-overwrite/.claude/hooks/lint-on-edit.sh +2 -0
- package/typescript/copy-overwrite/.claude/hooks/sg-scan-on-edit.sh +68 -0
- package/typescript/copy-overwrite/.claude/settings.json +79 -0
- package/typescript/copy-overwrite/.claude/skills/jsdoc-best-practices/SKILL.md +44 -0
- package/typescript/copy-overwrite/.github/README.md +49 -1
- package/typescript/copy-overwrite/.github/dependabot.yml +3 -0
- package/typescript/copy-overwrite/.github/workflows/ci.yml +7 -29
- package/typescript/copy-overwrite/.github/workflows/claude.yml +3 -0
- package/typescript/copy-overwrite/.github/workflows/create-github-issue-on-failure.yml +6 -4
- package/typescript/copy-overwrite/.github/workflows/create-issue-on-failure.yml +176 -0
- package/typescript/copy-overwrite/.github/workflows/create-jira-issue-on-failure.yml +3 -1
- package/typescript/copy-overwrite/.github/workflows/create-sentry-issue-on-failure.yml +3 -1
- package/typescript/copy-overwrite/.github/workflows/lint-slow.yml +40 -0
- package/typescript/copy-overwrite/.github/workflows/quality.yml +151 -38
- package/typescript/copy-overwrite/.github/workflows/release.yml +3 -0
- package/typescript/copy-overwrite/.gitleaksignore +3 -0
- package/typescript/copy-overwrite/.lintstagedrc.json +6 -0
- package/typescript/copy-overwrite/.prettierignore +2 -1
- package/typescript/copy-overwrite/.yamllint +2 -0
- package/typescript/copy-overwrite/ast-grep/rule-tests/.gitkeep +3 -0
- package/typescript/copy-overwrite/ast-grep/rules/.gitkeep +3 -0
- package/typescript/copy-overwrite/ast-grep/utils/.gitkeep +3 -0
- package/typescript/copy-overwrite/{commitlint.config.js → commitlint.config.cjs} +5 -0
- package/typescript/copy-overwrite/eslint-plugin-code-organization/__tests__/enforce-statement-order.test.js +5 -0
- package/typescript/copy-overwrite/eslint-plugin-code-organization/index.js +5 -0
- package/typescript/copy-overwrite/eslint-plugin-code-organization/rules/enforce-statement-order.js +5 -0
- package/typescript/copy-overwrite/eslint.base.ts +430 -0
- package/typescript/copy-overwrite/eslint.config.ts +52 -0
- package/typescript/copy-overwrite/eslint.ignore.config.json +19 -2
- package/typescript/copy-overwrite/eslint.slow.config.ts +69 -0
- package/typescript/copy-overwrite/eslint.typescript.ts +142 -0
- package/typescript/copy-overwrite/knip.json +64 -0
- package/typescript/copy-overwrite/sgconfig.yml +11 -0
- package/typescript/copy-overwrite/tsconfig.eslint.json +9 -0
- package/typescript/create-only/eslint.config.local.ts +24 -0
- package/typescript/{copy-overwrite/eslint.thresholds.config.json → create-only/eslint.thresholds.json} +1 -1
- package/typescript/github-rulesets/base.json +2 -75
- package/typescript/merge/.claude/settings.json +160 -0
- package/typescript/merge/package.json +35 -34
- package/all/copy-overwrite/.claude/commands/rules/format-md.md +0 -72
- package/all/copy-overwrite/.claude/skills/coding-philosophy/references/function-structure.md +0 -416
- package/all/copy-overwrite/.claude/skills/coding-philosophy/references/immutable-patterns.md +0 -316
- package/expo/copy-overwrite/eslint-plugin-ui-standards/rules/no-inline-styles.js +0 -73
- package/expo/copy-overwrite/eslint.config.mjs +0 -560
- package/lisa.sh +0 -35
- package/typescript/copy-overwrite/eslint.config.mjs +0 -390
- /package/{all/create-only/PROJECT_RULES.md → cdk/copy-overwrite/.github/workflows/.keep} +0 -0
- /package/{typescript → nestjs}/copy-overwrite/.github/k6/BROWSER_TESTING_NOTE.md +0 -0
- /package/{typescript → nestjs}/copy-overwrite/.github/k6/INTEGRATION_GUIDE.md +0 -0
- /package/{typescript → nestjs}/copy-overwrite/.github/k6/SCENARIO_SELECTION_GUIDE.md +0 -0
- /package/{typescript → nestjs}/copy-overwrite/.github/k6/scenarios/load.json +0 -0
- /package/{typescript → nestjs}/copy-overwrite/.github/k6/scenarios/smoke.json +0 -0
- /package/{typescript → nestjs}/copy-overwrite/.github/k6/scenarios/soak.json +0 -0
- /package/{typescript → nestjs}/copy-overwrite/.github/k6/scenarios/spike.json +0 -0
- /package/{typescript → nestjs}/copy-overwrite/.github/k6/scenarios/stress.json +0 -0
- /package/{typescript → nestjs}/copy-overwrite/.github/k6/thresholds/normal.json +0 -0
- /package/{typescript → nestjs}/copy-overwrite/.github/k6/thresholds/relaxed.json +0 -0
- /package/{typescript → nestjs}/copy-overwrite/.github/k6/thresholds/strict.json +0 -0
- /package/{typescript → nestjs}/copy-overwrite/.github/workflows/k6-load-test-README.md +0 -0
package/dist/core/lisa.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
1
|
+
/* eslint-disable max-lines -- Main orchestrator class with apply/uninstall/validate operations */
|
|
2
|
+
import * as fse from "fs-extra";
|
|
3
|
+
import { readdir, rmdir, stat } from "node:fs/promises";
|
|
4
|
+
import * as path from "node:path";
|
|
5
|
+
import pc from "picocolors";
|
|
6
|
+
import { DestinationNotDirectoryError, DestinationNotFoundError, UserAbortedError, } from "../errors/index.js";
|
|
7
|
+
import { listFilesRecursive } from "../utils/file-operations.js";
|
|
8
|
+
import { COPY_STRATEGIES, createInitialCounters } from "./config.js";
|
|
8
9
|
/**
|
|
9
10
|
* Main Lisa orchestrator
|
|
10
11
|
*/
|
|
@@ -13,78 +14,208 @@ export class Lisa {
|
|
|
13
14
|
deps;
|
|
14
15
|
counters = createInitialCounters();
|
|
15
16
|
detectedTypes = [];
|
|
17
|
+
separator = "========================================";
|
|
18
|
+
dryRunPrefix = "Would copy:";
|
|
19
|
+
dryRunSkipMsg = "Would skip:";
|
|
20
|
+
dryRunPromptMsg = "Would prompt:";
|
|
21
|
+
dryRunAppendMsg = "Would append:";
|
|
22
|
+
dryRunMergeMsg = "Would merge:";
|
|
23
|
+
copyMsg = "Copied:";
|
|
24
|
+
skipMsg = "Skipped:";
|
|
25
|
+
promptMsg = "Overwritten:";
|
|
26
|
+
appendMsg = "Appended:";
|
|
27
|
+
mergeMsg = "Merged:";
|
|
28
|
+
/**
|
|
29
|
+
* Initialize Lisa orchestrator
|
|
30
|
+
* @param config - Configuration for the apply/uninstall operation
|
|
31
|
+
* @param deps - Injected service dependencies
|
|
32
|
+
*/
|
|
16
33
|
constructor(config, deps) {
|
|
17
34
|
this.config = config;
|
|
18
35
|
this.deps = deps;
|
|
19
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* Initialize services
|
|
39
|
+
*/
|
|
40
|
+
async initServices() {
|
|
41
|
+
const { backupService, manifestService } = this.deps;
|
|
42
|
+
if (!this.config.dryRun) {
|
|
43
|
+
await backupService.init(this.config.destDir);
|
|
44
|
+
await manifestService.init(this.config.destDir, this.config.lisaDir);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Detect and confirm project types
|
|
49
|
+
*/
|
|
50
|
+
async detectTypes() {
|
|
51
|
+
const { detectorRegistry, prompter } = this.deps;
|
|
52
|
+
const rawTypes = await detectorRegistry.detectAll(this.config.destDir);
|
|
53
|
+
this.detectedTypes = detectorRegistry.expandAndOrderTypes(rawTypes);
|
|
54
|
+
this.detectedTypes = [
|
|
55
|
+
...(await prompter.confirmProjectTypes(this.detectedTypes)),
|
|
56
|
+
];
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Process all configurations
|
|
60
|
+
*/
|
|
61
|
+
async processConfigurations() {
|
|
62
|
+
const { logger } = this.deps;
|
|
63
|
+
logger.info("Processing common configurations (all/)...");
|
|
64
|
+
await this.processProjectType("all");
|
|
65
|
+
for (const type of this.detectedTypes) {
|
|
66
|
+
const typeDir = path.join(this.config.lisaDir, type);
|
|
67
|
+
if (await fse.pathExists(typeDir)) {
|
|
68
|
+
logger.info(`Processing ${type} configurations...`);
|
|
69
|
+
await this.processProjectType(type);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
logger.warn(`No configuration directory found for type: ${type}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Process deletions from deletions.json files
|
|
78
|
+
*/
|
|
79
|
+
async processDeletions() {
|
|
80
|
+
const { logger } = this.deps;
|
|
81
|
+
// Process deletions for "all" and each detected type
|
|
82
|
+
const typesToProcess = ["all", ...this.detectedTypes];
|
|
83
|
+
for (const type of typesToProcess) {
|
|
84
|
+
const deletionsPath = path.join(this.config.lisaDir, type, "deletions.json");
|
|
85
|
+
if (await fse.pathExists(deletionsPath)) {
|
|
86
|
+
logger.info(`Processing deletions for ${type}...`);
|
|
87
|
+
await this.processDeletionsFile(deletionsPath);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Process a single deletions.json file
|
|
93
|
+
* @param deletionsPath - Path to the deletions.json file
|
|
94
|
+
*/
|
|
95
|
+
async processDeletionsFile(deletionsPath) {
|
|
96
|
+
const { logger } = this.deps;
|
|
97
|
+
try {
|
|
98
|
+
const content = await fse.readFile(deletionsPath, "utf-8");
|
|
99
|
+
const deletions = JSON.parse(content);
|
|
100
|
+
if (!Array.isArray(deletions.paths)) {
|
|
101
|
+
logger.warn(`Invalid deletions.json format: paths must be an array`);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
for (const relativePath of deletions.paths) {
|
|
105
|
+
await this.processSingleDeletion(relativePath);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
110
|
+
logger.warn(`Failed to process deletions file: ${message}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Process a single deletion path
|
|
115
|
+
* @param relativePath - Relative path to delete
|
|
116
|
+
*/
|
|
117
|
+
async processSingleDeletion(relativePath) {
|
|
118
|
+
const { logger } = this.deps;
|
|
119
|
+
const targetPath = path.join(this.config.destDir, relativePath);
|
|
120
|
+
const resolvedTarget = path.resolve(targetPath);
|
|
121
|
+
const resolvedDest = path.resolve(this.config.destDir);
|
|
122
|
+
// Explicit guard: disallow deleting the project root itself
|
|
123
|
+
if (resolvedTarget === resolvedDest) {
|
|
124
|
+
logger.warn(`Skipping deletion of project root directory: ${relativePath || "."}`);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
// Safety check: only allow paths strictly inside destDir
|
|
128
|
+
if (!resolvedTarget.startsWith(resolvedDest + path.sep)) {
|
|
129
|
+
logger.warn(`Skipping deletion outside project directory: ${relativePath}`);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (!(await fse.pathExists(targetPath))) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
if (this.config.dryRun) {
|
|
136
|
+
logger.dry(`Would delete: ${relativePath}`);
|
|
137
|
+
this.counters.deleted++;
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
await fse.remove(targetPath);
|
|
141
|
+
logger.success(`Deleted: ${relativePath}`);
|
|
142
|
+
this.counters.deleted++;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Finalize operation
|
|
147
|
+
* @returns Promise that resolves when finalization is complete
|
|
148
|
+
*/
|
|
149
|
+
async finalize() {
|
|
150
|
+
const { manifestService, backupService } = this.deps;
|
|
151
|
+
if (!this.config.dryRun) {
|
|
152
|
+
await manifestService.finalize();
|
|
153
|
+
await backupService.cleanup();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Create success result
|
|
158
|
+
* @returns Success result with operation counters and detected types
|
|
159
|
+
*/
|
|
160
|
+
getSuccessResult() {
|
|
161
|
+
const mode = this.config.validateOnly ? "validate" : "apply";
|
|
162
|
+
return {
|
|
163
|
+
success: true,
|
|
164
|
+
counters: { ...this.counters },
|
|
165
|
+
detectedTypes: [...this.detectedTypes],
|
|
166
|
+
mode,
|
|
167
|
+
errors: [],
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Handle apply error and rollback
|
|
172
|
+
* @param error Error that occurred during apply
|
|
173
|
+
* @returns Error result
|
|
174
|
+
*/
|
|
175
|
+
async handleApplyError(error) {
|
|
176
|
+
const { backupService } = this.deps;
|
|
177
|
+
if (!this.config.dryRun) {
|
|
178
|
+
try {
|
|
179
|
+
await backupService.rollback();
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
// Rollback error already logged
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
const mode = this.config.validateOnly ? "validate" : "apply";
|
|
186
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
187
|
+
return {
|
|
188
|
+
success: false,
|
|
189
|
+
counters: { ...this.counters },
|
|
190
|
+
detectedTypes: [...this.detectedTypes],
|
|
191
|
+
mode,
|
|
192
|
+
errors: [message],
|
|
193
|
+
};
|
|
194
|
+
}
|
|
20
195
|
/**
|
|
21
196
|
* Apply Lisa configurations to the destination project
|
|
197
|
+
* @returns Result of the apply operation
|
|
22
198
|
*/
|
|
23
199
|
async apply() {
|
|
24
|
-
const { logger, prompter, manifestService, backupService, detectorRegistry } = this.deps;
|
|
25
200
|
try {
|
|
26
|
-
// Validate destination
|
|
27
201
|
await this.validateDestination();
|
|
28
|
-
|
|
202
|
+
await this.validateGitState();
|
|
29
203
|
this.printHeader();
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
// Detect project types
|
|
36
|
-
const rawTypes = await detectorRegistry.detectAll(this.config.destDir);
|
|
37
|
-
this.detectedTypes = detectorRegistry.expandAndOrderTypes(rawTypes);
|
|
38
|
-
// Confirm with user
|
|
39
|
-
this.detectedTypes = [...(await prompter.confirmProjectTypes(this.detectedTypes))];
|
|
40
|
-
// Process 'all' directory first
|
|
41
|
-
logger.info('Processing common configurations (all/)...');
|
|
42
|
-
await this.processProjectType('all');
|
|
43
|
-
// Process each detected type
|
|
44
|
-
for (const type of this.detectedTypes) {
|
|
45
|
-
const typeDir = path.join(this.config.lisaDir, type);
|
|
46
|
-
if (await fse.pathExists(typeDir)) {
|
|
47
|
-
logger.info(`Processing ${type} configurations...`);
|
|
48
|
-
await this.processProjectType(type);
|
|
49
|
-
}
|
|
50
|
-
else {
|
|
51
|
-
logger.warn(`No configuration directory found for type: ${type}`);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
// Finalize manifest
|
|
55
|
-
if (!this.config.dryRun) {
|
|
56
|
-
await manifestService.finalize();
|
|
57
|
-
await backupService.cleanup();
|
|
58
|
-
}
|
|
204
|
+
await this.initServices();
|
|
205
|
+
await this.detectTypes();
|
|
206
|
+
await this.processConfigurations();
|
|
207
|
+
await this.processDeletions();
|
|
208
|
+
await this.finalize();
|
|
59
209
|
this.printSummary();
|
|
60
|
-
return
|
|
61
|
-
success: true,
|
|
62
|
-
counters: { ...this.counters },
|
|
63
|
-
detectedTypes: [...this.detectedTypes],
|
|
64
|
-
mode: this.config.validateOnly ? 'validate' : 'apply',
|
|
65
|
-
errors: [],
|
|
66
|
-
};
|
|
210
|
+
return this.getSuccessResult();
|
|
67
211
|
}
|
|
68
212
|
catch (error) {
|
|
69
|
-
|
|
70
|
-
try {
|
|
71
|
-
await backupService.rollback();
|
|
72
|
-
}
|
|
73
|
-
catch {
|
|
74
|
-
// Rollback error already logged
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
return {
|
|
78
|
-
success: false,
|
|
79
|
-
counters: { ...this.counters },
|
|
80
|
-
detectedTypes: [...this.detectedTypes],
|
|
81
|
-
mode: this.config.validateOnly ? 'validate' : 'apply',
|
|
82
|
-
errors: [error instanceof Error ? error.message : String(error)],
|
|
83
|
-
};
|
|
213
|
+
return this.handleApplyError(error);
|
|
84
214
|
}
|
|
85
215
|
}
|
|
86
216
|
/**
|
|
87
217
|
* Validate compatibility without applying changes
|
|
218
|
+
* @returns Result of the validate operation
|
|
88
219
|
*/
|
|
89
220
|
async validate() {
|
|
90
221
|
// Validate mode is essentially a dry run
|
|
@@ -92,80 +223,52 @@ export class Lisa {
|
|
|
92
223
|
}
|
|
93
224
|
/**
|
|
94
225
|
* Uninstall Lisa-managed files from the project
|
|
226
|
+
* @returns Result of the uninstall operation with removal statistics
|
|
95
227
|
*/
|
|
96
228
|
async uninstall() {
|
|
97
229
|
const { logger, manifestService } = this.deps;
|
|
98
230
|
// Validate destination
|
|
99
231
|
await this.validateDestination();
|
|
100
232
|
// Print header
|
|
101
|
-
console.log(
|
|
102
|
-
console.log(pc.blue(
|
|
103
|
-
console.log(pc.blue(
|
|
104
|
-
console.log(pc.blue(
|
|
105
|
-
console.log(
|
|
106
|
-
let removed = 0;
|
|
107
|
-
let skipped = 0;
|
|
233
|
+
console.log("");
|
|
234
|
+
console.log(pc.blue(this.separator));
|
|
235
|
+
console.log(pc.blue(" Lisa Uninstaller"));
|
|
236
|
+
console.log(pc.blue(this.separator));
|
|
237
|
+
console.log("");
|
|
108
238
|
const errors = [];
|
|
109
239
|
try {
|
|
110
240
|
// Read manifest
|
|
111
241
|
const entries = await manifestService.read(this.config.destDir);
|
|
112
|
-
logger.info(`Reading manifest: ${path.join(this.config.destDir,
|
|
113
|
-
console.log(
|
|
114
|
-
// Process
|
|
115
|
-
|
|
116
|
-
const destFile = path.join(this.config.destDir, entry.relativePath);
|
|
117
|
-
switch (entry.strategy) {
|
|
118
|
-
case 'copy-overwrite':
|
|
119
|
-
case 'create-only':
|
|
120
|
-
// These files can be safely removed
|
|
121
|
-
if (await fse.pathExists(destFile)) {
|
|
122
|
-
if (this.config.dryRun) {
|
|
123
|
-
logger.dry(`Would remove: ${entry.relativePath}`);
|
|
124
|
-
}
|
|
125
|
-
else {
|
|
126
|
-
await fse.remove(destFile);
|
|
127
|
-
logger.success(`Removed: ${entry.relativePath}`);
|
|
128
|
-
}
|
|
129
|
-
removed++;
|
|
130
|
-
}
|
|
131
|
-
else {
|
|
132
|
-
skipped++;
|
|
133
|
-
}
|
|
134
|
-
break;
|
|
135
|
-
case 'copy-contents':
|
|
136
|
-
// Cannot safely remove - would need to diff
|
|
137
|
-
logger.warn(`Cannot auto-remove (copy-contents): ${entry.relativePath}`);
|
|
138
|
-
logger.info(' Manually review and remove added lines if needed.');
|
|
139
|
-
skipped++;
|
|
140
|
-
break;
|
|
141
|
-
case 'merge':
|
|
142
|
-
// Cannot safely remove merged JSON
|
|
143
|
-
logger.warn(`Cannot auto-remove (merged JSON): ${entry.relativePath}`);
|
|
144
|
-
logger.info(' Manually remove Lisa-added keys if needed.');
|
|
145
|
-
skipped++;
|
|
146
|
-
break;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
242
|
+
logger.info(`Reading manifest: ${path.join(this.config.destDir, ".lisa-manifest")}`);
|
|
243
|
+
console.log("");
|
|
244
|
+
// Process entries
|
|
245
|
+
const stats = await this.processUninstallEntries(entries);
|
|
149
246
|
// Remove empty directories and manifest
|
|
150
247
|
if (!this.config.dryRun) {
|
|
151
248
|
await this.removeEmptyDirectories();
|
|
152
249
|
await manifestService.remove(this.config.destDir);
|
|
153
|
-
logger.success(
|
|
250
|
+
logger.success("Removed manifest file");
|
|
154
251
|
}
|
|
155
252
|
// Print summary
|
|
156
|
-
console.log(
|
|
157
|
-
console.log(pc.green(
|
|
158
|
-
console.log(pc.green(this.config.dryRun
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
console.log(
|
|
162
|
-
console.log(
|
|
163
|
-
console.log(
|
|
253
|
+
console.log("");
|
|
254
|
+
console.log(pc.green(this.separator));
|
|
255
|
+
console.log(pc.green(this.config.dryRun
|
|
256
|
+
? " Lisa Uninstall Dry Run Complete"
|
|
257
|
+
: " Lisa Uninstall Complete!"));
|
|
258
|
+
console.log(pc.green(this.separator));
|
|
259
|
+
console.log("");
|
|
260
|
+
console.log(` ${pc.green("Removed:")} ${String(stats.removed).padStart(3)} files`);
|
|
261
|
+
console.log(` ${pc.yellow("Skipped:")} ${String(stats.skipped).padStart(3)} files (manual review needed)`);
|
|
262
|
+
console.log("");
|
|
164
263
|
return {
|
|
165
264
|
success: true,
|
|
166
|
-
counters: {
|
|
265
|
+
counters: {
|
|
266
|
+
...this.counters,
|
|
267
|
+
copied: stats.removed,
|
|
268
|
+
skipped: stats.skipped,
|
|
269
|
+
},
|
|
167
270
|
detectedTypes: [],
|
|
168
|
-
mode:
|
|
271
|
+
mode: "uninstall",
|
|
169
272
|
errors,
|
|
170
273
|
};
|
|
171
274
|
}
|
|
@@ -177,13 +280,64 @@ export class Lisa {
|
|
|
177
280
|
success: false,
|
|
178
281
|
counters: this.counters,
|
|
179
282
|
detectedTypes: [],
|
|
180
|
-
mode:
|
|
283
|
+
mode: "uninstall",
|
|
181
284
|
errors,
|
|
182
285
|
};
|
|
183
286
|
}
|
|
184
287
|
}
|
|
288
|
+
/**
|
|
289
|
+
* Process single uninstall entry and return updated counts
|
|
290
|
+
* @param entry - Manifest entry to process for removal
|
|
291
|
+
* @param stats - Current removal statistics
|
|
292
|
+
* @param stats.removed - Number of files removed so far
|
|
293
|
+
* @param stats.skipped - Number of files skipped so far
|
|
294
|
+
* @returns Updated removal statistics after processing this entry
|
|
295
|
+
*/
|
|
296
|
+
async processEntry(entry, stats) {
|
|
297
|
+
const { logger } = this.deps;
|
|
298
|
+
const destFile = path.join(this.config.destDir, entry.relativePath);
|
|
299
|
+
switch (entry.strategy) {
|
|
300
|
+
case "copy-overwrite":
|
|
301
|
+
case "create-only": {
|
|
302
|
+
if (await fse.pathExists(destFile)) {
|
|
303
|
+
if (this.config.dryRun) {
|
|
304
|
+
logger.dry(`Would remove: ${entry.relativePath}`);
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
await fse.remove(destFile);
|
|
308
|
+
logger.success(`Removed: ${entry.relativePath}`);
|
|
309
|
+
}
|
|
310
|
+
return { ...stats, removed: stats.removed + 1 };
|
|
311
|
+
}
|
|
312
|
+
return { ...stats, skipped: stats.skipped + 1 };
|
|
313
|
+
}
|
|
314
|
+
case "copy-contents": {
|
|
315
|
+
logger.warn(`Cannot auto-remove (copy-contents): ${entry.relativePath}`);
|
|
316
|
+
logger.info(" Manually review and remove added lines if needed.");
|
|
317
|
+
return { ...stats, skipped: stats.skipped + 1 };
|
|
318
|
+
}
|
|
319
|
+
case "merge": {
|
|
320
|
+
logger.warn(`Cannot auto-remove (merged JSON): ${entry.relativePath}`);
|
|
321
|
+
logger.info(" Manually remove Lisa-added keys if needed.");
|
|
322
|
+
return { ...stats, skipped: stats.skipped + 1 };
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Process manifest entries during uninstall
|
|
328
|
+
* @param entries Manifest entries to process
|
|
329
|
+
* @returns Stats with removed and skipped counts
|
|
330
|
+
*/
|
|
331
|
+
async processUninstallEntries(entries) {
|
|
332
|
+
const initial = { removed: 0, skipped: 0 };
|
|
333
|
+
return await entries.reduce(async (statsPromise, entry) => {
|
|
334
|
+
const stats = await statsPromise;
|
|
335
|
+
return this.processEntry(entry, stats);
|
|
336
|
+
}, Promise.resolve(initial));
|
|
337
|
+
}
|
|
185
338
|
/**
|
|
186
339
|
* Process all files for a given project type
|
|
340
|
+
* @param type Project type to process
|
|
187
341
|
*/
|
|
188
342
|
async processProjectType(type) {
|
|
189
343
|
const { logger, strategyRegistry } = this.deps;
|
|
@@ -197,6 +351,10 @@ export class Lisa {
|
|
|
197
351
|
}
|
|
198
352
|
/**
|
|
199
353
|
* Process all files in a directory with the given strategy
|
|
354
|
+
* @param srcDir Source directory
|
|
355
|
+
* @param strategy Strategy to apply
|
|
356
|
+
* @param strategy.name Strategy name
|
|
357
|
+
* @param strategy.apply Apply function
|
|
200
358
|
*/
|
|
201
359
|
async processDirectory(srcDir, strategy) {
|
|
202
360
|
const files = await listFilesRecursive(srcDir);
|
|
@@ -207,6 +365,7 @@ export class Lisa {
|
|
|
207
365
|
},
|
|
208
366
|
backupFile: async (absolutePath) => {
|
|
209
367
|
await this.deps.backupService.backup(absolutePath);
|
|
368
|
+
await this.deps.backupService.persistentBackup(absolutePath);
|
|
210
369
|
},
|
|
211
370
|
promptOverwrite: async (relativePath, sourcePath, destPath) => {
|
|
212
371
|
return this.handleOverwritePrompt(relativePath, sourcePath, destPath);
|
|
@@ -222,16 +381,20 @@ export class Lisa {
|
|
|
222
381
|
}
|
|
223
382
|
/**
|
|
224
383
|
* Handle overwrite prompt for conflicting files
|
|
384
|
+
* @param relativePath Relative path of the file
|
|
385
|
+
* @param sourcePath Source file path
|
|
386
|
+
* @param destPath Destination file path
|
|
387
|
+
* @returns True if user approves overwrite
|
|
225
388
|
*/
|
|
226
389
|
async handleOverwritePrompt(relativePath, sourcePath, destPath) {
|
|
227
390
|
const { logger, prompter } = this.deps;
|
|
228
391
|
const decision = await prompter.promptOverwrite(relativePath);
|
|
229
|
-
if (decision ===
|
|
392
|
+
if (decision === "diff") {
|
|
230
393
|
// Show diff and re-prompt
|
|
231
394
|
await this.showDiff(sourcePath, destPath);
|
|
232
395
|
return this.handleOverwritePrompt(relativePath, sourcePath, destPath);
|
|
233
396
|
}
|
|
234
|
-
if (decision ===
|
|
397
|
+
if (decision === "yes") {
|
|
235
398
|
logger.info(`Auto-accepting overwrite (non-interactive): ${relativePath}`);
|
|
236
399
|
return true;
|
|
237
400
|
}
|
|
@@ -239,89 +402,78 @@ export class Lisa {
|
|
|
239
402
|
}
|
|
240
403
|
/**
|
|
241
404
|
* Show diff between two files
|
|
405
|
+
* @param sourcePath Source file path
|
|
406
|
+
* @param destPath Destination file path
|
|
242
407
|
*/
|
|
243
408
|
async showDiff(sourcePath, destPath) {
|
|
244
|
-
const { spawn } = await import(
|
|
245
|
-
return new Promise(
|
|
246
|
-
console.log(
|
|
247
|
-
const diff = spawn(
|
|
248
|
-
diff.on(
|
|
249
|
-
console.log(
|
|
409
|
+
const { spawn } = await import("node:child_process");
|
|
410
|
+
return new Promise(resolve => {
|
|
411
|
+
console.log("--- Differences ---");
|
|
412
|
+
const diff = spawn("diff", [destPath, sourcePath], { stdio: "inherit" });
|
|
413
|
+
diff.on("close", () => {
|
|
414
|
+
console.log("-------------------");
|
|
250
415
|
resolve();
|
|
251
416
|
});
|
|
252
417
|
});
|
|
253
418
|
}
|
|
254
419
|
/**
|
|
255
420
|
* Update counters based on operation result
|
|
421
|
+
* @param result Operation result to count
|
|
256
422
|
*/
|
|
257
423
|
updateCounters(result) {
|
|
258
424
|
switch (result.action) {
|
|
259
|
-
case
|
|
260
|
-
case
|
|
425
|
+
case "copied":
|
|
426
|
+
case "created":
|
|
261
427
|
this.counters.copied++;
|
|
262
428
|
break;
|
|
263
|
-
case
|
|
429
|
+
case "skipped":
|
|
264
430
|
this.counters.skipped++;
|
|
265
431
|
break;
|
|
266
|
-
case
|
|
432
|
+
case "overwritten":
|
|
267
433
|
this.counters.overwritten++;
|
|
268
434
|
break;
|
|
269
|
-
case
|
|
435
|
+
case "appended":
|
|
270
436
|
this.counters.appended++;
|
|
271
437
|
break;
|
|
272
|
-
case
|
|
438
|
+
case "merged":
|
|
273
439
|
this.counters.merged++;
|
|
274
440
|
break;
|
|
275
441
|
}
|
|
276
442
|
}
|
|
443
|
+
/**
|
|
444
|
+
* Log a message using appropriate logger method
|
|
445
|
+
* @param dryMsg Message for dry run mode
|
|
446
|
+
* @param successMsg Message for success mode
|
|
447
|
+
*/
|
|
448
|
+
logMessage(dryMsg, successMsg) {
|
|
449
|
+
const { logger } = this.deps;
|
|
450
|
+
this.config.dryRun ? logger.dry(dryMsg) : logger.success(successMsg);
|
|
451
|
+
}
|
|
277
452
|
/**
|
|
278
453
|
* Log operation result
|
|
454
|
+
* @param result Result to log
|
|
279
455
|
*/
|
|
280
456
|
logResult(result) {
|
|
281
|
-
const { logger } = this.deps;
|
|
282
457
|
switch (result.action) {
|
|
283
|
-
case
|
|
284
|
-
|
|
285
|
-
logger.dry(`Would copy: ${result.relativePath}`);
|
|
286
|
-
}
|
|
287
|
-
else {
|
|
288
|
-
logger.success(`Copied: ${result.relativePath}`);
|
|
289
|
-
}
|
|
458
|
+
case "copied":
|
|
459
|
+
this.logMessage(`Would copy: ${result.relativePath}`, `Copied: ${result.relativePath}`);
|
|
290
460
|
break;
|
|
291
|
-
case
|
|
292
|
-
|
|
293
|
-
logger.dry(`Would create: ${result.relativePath}`);
|
|
294
|
-
}
|
|
295
|
-
else {
|
|
296
|
-
logger.success(`Created: ${result.relativePath}`);
|
|
297
|
-
}
|
|
461
|
+
case "created":
|
|
462
|
+
this.logMessage(`Would create: ${result.relativePath}`, `Created: ${result.relativePath}`);
|
|
298
463
|
break;
|
|
299
|
-
case
|
|
464
|
+
case "skipped":
|
|
300
465
|
// Silent for skipped files
|
|
301
466
|
break;
|
|
302
|
-
case
|
|
303
|
-
|
|
304
|
-
logger.dry(`Would prompt to overwrite: ${result.relativePath}`);
|
|
305
|
-
}
|
|
306
|
-
else {
|
|
307
|
-
logger.success(`Overwritten: ${result.relativePath}`);
|
|
308
|
-
}
|
|
467
|
+
case "overwritten":
|
|
468
|
+
this.logMessage(`Would prompt to overwrite: ${result.relativePath}`, `Overwritten: ${result.relativePath}`);
|
|
309
469
|
break;
|
|
310
|
-
case
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
}
|
|
314
|
-
else {
|
|
315
|
-
logger.success(`Appended ${result.linesAdded} lines to: ${result.relativePath}`);
|
|
316
|
-
}
|
|
470
|
+
case "appended": {
|
|
471
|
+
const msg = `Appended ${result.linesAdded} lines to: ${result.relativePath}`;
|
|
472
|
+
this.logMessage(`Would ${msg}`, msg);
|
|
317
473
|
break;
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
}
|
|
322
|
-
else {
|
|
323
|
-
logger.success(`Merged: ${result.relativePath}`);
|
|
324
|
-
}
|
|
474
|
+
}
|
|
475
|
+
case "merged":
|
|
476
|
+
this.logMessage(`Would merge: ${result.relativePath}`, `Merged: ${result.relativePath}`);
|
|
325
477
|
break;
|
|
326
478
|
}
|
|
327
479
|
}
|
|
@@ -338,93 +490,140 @@ export class Lisa {
|
|
|
338
490
|
throw new DestinationNotDirectoryError(destDir);
|
|
339
491
|
}
|
|
340
492
|
}
|
|
493
|
+
/**
|
|
494
|
+
* Check for uncommitted git changes and prompt user if dirty
|
|
495
|
+
*/
|
|
496
|
+
async validateGitState() {
|
|
497
|
+
const { gitService, prompter, logger } = this.deps;
|
|
498
|
+
const { destDir } = this.config;
|
|
499
|
+
// Skip git check if not a git repository
|
|
500
|
+
if (!(await gitService.isRepository(destDir))) {
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
// Check for uncommitted changes
|
|
504
|
+
if (!(await gitService.isDirty(destDir))) {
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
const status = await gitService.getStatus(destDir);
|
|
508
|
+
logger.warn("Git working directory has uncommitted changes");
|
|
509
|
+
// Always prompt for dirty git, even in yesMode
|
|
510
|
+
const proceed = await prompter.confirmDirtyGit(status);
|
|
511
|
+
if (!proceed) {
|
|
512
|
+
throw new UserAbortedError("Aborted: please commit or stash your changes before running Lisa");
|
|
513
|
+
}
|
|
514
|
+
}
|
|
341
515
|
/**
|
|
342
516
|
* Print header banner
|
|
343
517
|
*/
|
|
344
518
|
printHeader() {
|
|
345
519
|
const { logger } = this.deps;
|
|
346
|
-
console.log(
|
|
347
|
-
console.log(pc.blue(
|
|
520
|
+
console.log("");
|
|
521
|
+
console.log(pc.blue(this.separator));
|
|
348
522
|
if (this.config.validateOnly) {
|
|
349
|
-
console.log(pc.blue(
|
|
523
|
+
console.log(pc.blue(" Lisa Project Bootstrapper (VALIDATE)"));
|
|
350
524
|
}
|
|
351
525
|
else if (this.config.dryRun) {
|
|
352
|
-
console.log(pc.blue(
|
|
526
|
+
console.log(pc.blue(" Lisa Project Bootstrapper (DRY RUN)"));
|
|
353
527
|
}
|
|
354
528
|
else {
|
|
355
|
-
console.log(pc.blue(
|
|
529
|
+
console.log(pc.blue(" Lisa Project Bootstrapper"));
|
|
356
530
|
}
|
|
357
|
-
console.log(pc.blue(
|
|
358
|
-
console.log(
|
|
531
|
+
console.log(pc.blue(this.separator));
|
|
532
|
+
console.log("");
|
|
359
533
|
if (this.config.validateOnly) {
|
|
360
|
-
logger.info(
|
|
534
|
+
logger.info("Validate mode - checking project compatibility");
|
|
361
535
|
}
|
|
362
536
|
else if (this.config.dryRun) {
|
|
363
|
-
logger.warn(
|
|
537
|
+
logger.warn("Dry run mode - no changes will be made");
|
|
364
538
|
}
|
|
365
539
|
logger.info(`Lisa directory: ${this.config.lisaDir}`);
|
|
366
540
|
logger.info(`Destination: ${this.config.destDir}`);
|
|
367
|
-
console.log(
|
|
541
|
+
console.log("");
|
|
368
542
|
}
|
|
369
543
|
/**
|
|
370
|
-
* Print summary
|
|
544
|
+
* Print summary header with mode
|
|
371
545
|
*/
|
|
372
|
-
|
|
373
|
-
console.log(
|
|
374
|
-
console.log(pc.green(
|
|
546
|
+
printSummaryHeader() {
|
|
547
|
+
console.log("");
|
|
548
|
+
console.log(pc.green(this.separator));
|
|
375
549
|
if (this.config.validateOnly) {
|
|
376
|
-
console.log(pc.green(
|
|
550
|
+
console.log(pc.green(" Lisa Validation Complete"));
|
|
377
551
|
}
|
|
378
552
|
else if (this.config.dryRun) {
|
|
379
|
-
console.log(pc.green(
|
|
553
|
+
console.log(pc.green(" Lisa Dry Run Complete"));
|
|
380
554
|
}
|
|
381
555
|
else {
|
|
382
|
-
console.log(pc.green(
|
|
556
|
+
console.log(pc.green(" Lisa Installation Complete!"));
|
|
383
557
|
}
|
|
384
|
-
console.log(pc.green(
|
|
385
|
-
console.log(
|
|
386
|
-
|
|
558
|
+
console.log(pc.green(this.separator));
|
|
559
|
+
console.log("");
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Print summary statistics
|
|
563
|
+
*/
|
|
564
|
+
printSummaryStats() {
|
|
565
|
+
const { copied, skipped, overwritten, appended, merged, deleted } = this.counters;
|
|
387
566
|
if (this.config.validateOnly) {
|
|
388
|
-
console.log(` ${pc.green(
|
|
389
|
-
console.log(` ${pc.blue(
|
|
390
|
-
console.log(` ${pc.yellow(
|
|
391
|
-
console.log(` ${pc.blue(
|
|
392
|
-
console.log(` ${pc.green(
|
|
567
|
+
console.log(` ${pc.green("Compatible files:")} ${String(copied).padStart(3)} files`);
|
|
568
|
+
console.log(` ${pc.blue("Already present:")} ${String(skipped).padStart(3)} files`);
|
|
569
|
+
console.log(` ${pc.yellow("Would conflict:")} ${String(overwritten).padStart(3)} files`);
|
|
570
|
+
console.log(` ${pc.blue("Would append:")} ${String(appended).padStart(3)} files`);
|
|
571
|
+
console.log(` ${pc.green("Would merge:")} ${String(merged).padStart(3)} files`);
|
|
572
|
+
console.log(` ${pc.red("Would delete:")} ${String(deleted).padStart(3)} files`);
|
|
393
573
|
}
|
|
394
574
|
else if (this.config.dryRun) {
|
|
395
|
-
console.log(` ${pc.green(
|
|
396
|
-
console.log(` ${pc.blue(
|
|
397
|
-
console.log(` ${pc.yellow(
|
|
398
|
-
console.log(` ${pc.blue(
|
|
399
|
-
console.log(` ${pc.green(
|
|
575
|
+
console.log(` ${pc.green(this.dryRunPrefix)} ${String(copied).padStart(3)} files`);
|
|
576
|
+
console.log(` ${pc.blue(this.dryRunSkipMsg)} ${String(skipped).padStart(3)} files (identical or create-only)`);
|
|
577
|
+
console.log(` ${pc.yellow(this.dryRunPromptMsg)} ${String(overwritten).padStart(3)} files (differ)`);
|
|
578
|
+
console.log(` ${pc.blue(this.dryRunAppendMsg)} ${String(appended).padStart(3)} files (copy-contents)`);
|
|
579
|
+
console.log(` ${pc.green(this.dryRunMergeMsg)} ${String(merged).padStart(3)} files (JSON)`);
|
|
580
|
+
console.log(` ${pc.red("Would delete:")} ${String(deleted).padStart(3)} files`);
|
|
400
581
|
}
|
|
401
582
|
else {
|
|
402
|
-
console.log(` ${pc.green(
|
|
403
|
-
console.log(` ${pc.blue(
|
|
404
|
-
console.log(` ${pc.yellow(
|
|
405
|
-
console.log(` ${pc.blue(
|
|
406
|
-
console.log(` ${pc.green(
|
|
583
|
+
console.log(` ${pc.green(this.copyMsg)} ${String(copied).padStart(3)} files`);
|
|
584
|
+
console.log(` ${pc.blue(this.skipMsg)} ${String(skipped).padStart(3)} files (identical or create-only)`);
|
|
585
|
+
console.log(` ${pc.yellow(this.promptMsg)} ${String(overwritten).padStart(3)} files (user approved)`);
|
|
586
|
+
console.log(` ${pc.blue(this.appendMsg)} ${String(appended).padStart(3)} files (copy-contents)`);
|
|
587
|
+
console.log(` ${pc.green(this.mergeMsg)} ${String(merged).padStart(3)} files (JSON merged)`);
|
|
588
|
+
console.log(` ${pc.red("Deleted:")} ${String(deleted).padStart(3)} files`);
|
|
407
589
|
}
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
590
|
+
}
|
|
591
|
+
/**
|
|
592
|
+
* Print project types
|
|
593
|
+
*/
|
|
594
|
+
printProjectTypes() {
|
|
595
|
+
console.log("");
|
|
596
|
+
const allAndDetected = this.detectedTypes.length > 0
|
|
597
|
+
? `all ${this.detectedTypes.join(" ")}`
|
|
598
|
+
: "all";
|
|
599
|
+
console.log(`Project types: ${pc.green(allAndDetected)}`);
|
|
600
|
+
console.log("");
|
|
601
|
+
}
|
|
602
|
+
/**
|
|
603
|
+
* Print validation result
|
|
604
|
+
*/
|
|
605
|
+
printValidationResult() {
|
|
606
|
+
if (!this.config.validateOnly)
|
|
607
|
+
return;
|
|
608
|
+
const { logger } = this.deps;
|
|
609
|
+
const { overwritten } = this.counters;
|
|
610
|
+
if (overwritten > 0) {
|
|
611
|
+
logger.warn(`Validation found ${overwritten} file(s) that would conflict`);
|
|
612
|
+
console.log("Run without --validate to apply changes interactively");
|
|
411
613
|
}
|
|
412
614
|
else {
|
|
413
|
-
|
|
414
|
-
}
|
|
415
|
-
console.log('');
|
|
416
|
-
// Validation result for CI/CD
|
|
417
|
-
if (this.config.validateOnly) {
|
|
418
|
-
const { logger } = this.deps;
|
|
419
|
-
if (overwritten > 0) {
|
|
420
|
-
logger.warn(`Validation found ${overwritten} file(s) that would conflict`);
|
|
421
|
-
console.log('Run without --validate to apply changes interactively');
|
|
422
|
-
}
|
|
423
|
-
else {
|
|
424
|
-
logger.success('Project is compatible with Lisa configurations');
|
|
425
|
-
}
|
|
615
|
+
logger.success("Project is compatible with Lisa configurations");
|
|
426
616
|
}
|
|
427
617
|
}
|
|
618
|
+
/**
|
|
619
|
+
* Print summary
|
|
620
|
+
*/
|
|
621
|
+
printSummary() {
|
|
622
|
+
this.printSummaryHeader();
|
|
623
|
+
this.printSummaryStats();
|
|
624
|
+
this.printProjectTypes();
|
|
625
|
+
this.printValidationResult();
|
|
626
|
+
}
|
|
428
627
|
/**
|
|
429
628
|
* Remove empty directories after uninstall
|
|
430
629
|
*/
|
|
@@ -433,7 +632,7 @@ export class Lisa {
|
|
|
433
632
|
const removeEmpty = async (dir) => {
|
|
434
633
|
if (dir === destDir)
|
|
435
634
|
return;
|
|
436
|
-
if (dir.includes(
|
|
635
|
+
if (dir.includes(".git") || dir.includes("node_modules"))
|
|
437
636
|
return;
|
|
438
637
|
try {
|
|
439
638
|
const entries = await readdir(dir);
|
|
@@ -450,10 +649,13 @@ export class Lisa {
|
|
|
450
649
|
// Get all directories and check if empty
|
|
451
650
|
const entries = await readdir(destDir, { withFileTypes: true });
|
|
452
651
|
for (const entry of entries) {
|
|
453
|
-
if (entry.isDirectory() &&
|
|
652
|
+
if (entry.isDirectory() &&
|
|
653
|
+
!entry.name.startsWith(".git") &&
|
|
654
|
+
entry.name !== "node_modules") {
|
|
454
655
|
await removeEmpty(path.join(destDir, entry.name));
|
|
455
656
|
}
|
|
456
657
|
}
|
|
457
658
|
}
|
|
458
659
|
}
|
|
660
|
+
/* eslint-enable max-lines -- Main orchestrator class with apply/uninstall/validate operations */
|
|
459
661
|
//# sourceMappingURL=lisa.js.map
|