@1aboveio/skills 1.0.10 → 1.0.12
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/.codex-plugin/plugin.json +1 -1
- package/.codex-plugin/resources.json +298 -216
- package/README.md +3 -3
- package/package.json +10 -8
- package/skills/app-dev/airflow/SKILL.md +1 -1
- package/skills/app-dev/android-foundation/SKILL.md +35 -37
- package/skills/app-dev/android-foundation/references/delivery-focus.md +4 -2
- package/skills/app-dev/android-foundation/references/feature-work.md +50 -0
- package/skills/app-dev/android-foundation/references/setup.md +24 -9
- package/skills/app-dev/android-foundation/references/workspace.md +81 -0
- package/skills/app-dev/android-foundation/scripts/workspace-runtime/bin/workspaces.mjs +52 -0
- package/skills/app-dev/android-foundation/scripts/workspace-runtime/native-workspace.mjs +239 -0
- package/skills/app-dev/android-foundation/scripts/workspace-runtime/workspace-lifecycle.mjs +2154 -0
- package/skills/app-dev/app-debug/SKILL.md +1 -1
- package/skills/app-dev/better-auth/SKILL.md +1 -1
- package/skills/app-dev/data-catalog/SKILL.md +1 -1
- package/skills/app-dev/data-quality/SKILL.md +1 -1
- package/skills/app-dev/ios-foundation/SKILL.md +15 -10
- package/skills/app-dev/ios-foundation/references/setup.md +25 -9
- package/skills/app-dev/ios-foundation/references/workspace.md +82 -0
- package/skills/app-dev/ios-foundation/scripts/workspace-runtime/bin/workspaces.mjs +52 -0
- package/skills/app-dev/ios-foundation/scripts/workspace-runtime/native-workspace.mjs +239 -0
- package/skills/app-dev/ios-foundation/scripts/workspace-runtime/workspace-lifecycle.mjs +2154 -0
- package/skills/app-dev/monorepo/SKILL.md +1 -1
- package/skills/app-dev/nextjs-fullstack/SKILL.md +1 -1
- package/skills/app-dev/prisma-setup/SKILL.md +1 -1
- package/skills/app-dev/pyspark/SKILL.md +1 -1
- package/skills/app-dev/python-backend/SKILL.md +1 -1
- package/skills/app-dev/shadcn/SKILL.md +1 -1
- package/skills/app-dev/zod-v4/SKILL.md +1 -1
- package/skills/cicd/cloud-build/SKILL.md +1 -1
- package/skills/cicd/cloud-debug/SKILL.md +1 -1
- package/skills/cicd/cloud-deploy/SKILL.md +1 -1
- package/skills/cicd/docker/SKILL.md +1 -1
- package/skills/cicd/github-actions/SKILL.md +1 -1
- package/skills/cicd/github-flow/SKILL.md +1 -1
- package/skills/cicd/google-cloud/SKILL.md +1 -1
- package/skills/cicd/mergify/SKILL.md +1 -1
- package/skills/cicd/podman/SKILL.md +1 -1
- package/skills/content/article-review/SKILL.md +1 -1
- package/skills/content/article-to-image/SKILL.md +1 -1
- package/skills/content/flowchart-svg/SKILL.md +1 -1
- package/skills/content/image-gen/SKILL.md +1 -1
- package/skills/content/mermaid/SKILL.md +1 -1
- package/skills/content/wechat-article-writer/SKILL.md +1 -1
- package/skills/content/wechat-publisher/SKILL.md +1 -1
- package/skills/content/wechat-search/SKILL.md +1 -1
- package/skills/content/xai-search/SKILL.md +1 -1
- package/skills/core/distill-writing/SKILL.md +3 -3
- package/skills/core/distill-writing/references/extraction.md +1 -1
- package/skills/core/doc-to-md/SKILL.md +1 -1
- package/skills/core/editorial-review/SKILL.md +82 -12
- package/skills/core/editorial-review/references/checklist.md +26 -1
- package/skills/core/house-style/SKILL.md +8 -3
- package/skills/core/house-style/references/analytical-reporting.md +123 -0
- package/skills/core/house-style/references/analytical-writing-patterns.md +79 -0
- package/skills/core/house-style/references/analytical-writing-research.md +129 -0
- package/skills/core/{write-doc → house-style}/references/bilingual.md +7 -5
- package/skills/core/house-style/references/definitions-and-metrics.md +86 -0
- package/skills/core/house-style/references/document-workflow.md +193 -0
- package/skills/core/house-style/references/initialization.md +66 -0
- package/skills/core/house-style/references/persistence.md +9 -4
- package/skills/core/house-style/references/rules.md +11 -4
- package/skills/core/house-style/scripts/init-guidance.mjs +213 -0
- package/skills/core/house-style/templates/analytical-report.md +54 -0
- package/skills/core/house-style/templates/gap-analysis.md +1 -1
- package/skills/core/plain-language/SKILL.md +2 -2
- package/skills/core/polish-cn/SKILL.md +9 -2
- package/skills/core/reg-parser/SKILL.md +3 -3
- package/skills/core/terminology/SKILL.md +8 -4
- package/skills/core/write-doc/SKILL.md +40 -151
- package/skills/core/write-report/SKILL.md +129 -0
- package/skills/core/{write-doc → write-report}/references/compliance-reports.md +4 -4
- package/skills/engineering/better-review/SKILL.md +37 -37
- package/skills/engineering/better-review/references/passes.md +35 -41
- package/skills/engineering/better-review/references/result.md +10 -9
- package/skills/engineering/e2e-test/SKILL.md +17 -17
- package/skills/engineering/e2e-test/references/authoring/playwright-config.md +11 -13
- package/skills/engineering/e2e-test/references/presentation-sweep.md +7 -7
- package/skills/engineering/e2e-test/references/reviewing.md +9 -11
- package/skills/engineering/e2e-test/references/route-manifest.md +1 -1
- package/skills/engineering/engineering/SKILL.md +201 -0
- package/skills/engineering/engineering/references/engineering-conventions.md +32 -0
- package/skills/engineering/engineering/references/feature-delivery.md +8 -0
- package/skills/engineering/engineering/references/init-scope.md +45 -0
- package/skills/engineering/engineering/references/scope-management.md +85 -0
- package/skills/engineering/engineering/scripts/init.mjs +160 -0
- package/skills/engineering/{contributing → engineering}/scripts/persist-conventions.mjs +1 -1
- package/skills/engineering/engineering-runtime/SKILL.md +1 -1
- package/skills/engineering/engineering-runtime/coherence/workflow.json +19 -19
- package/skills/engineering/engineering-runtime/scripts/workflow-policy.mjs +1 -1
- package/skills/engineering/ensure-coverage/SKILL.md +22 -109
- package/skills/engineering/ensure-coverage/references/breadth/coverage-ledger.md +7 -5
- package/skills/engineering/ensure-coverage/references/breadth/surface-baseline.md +4 -4
- package/skills/engineering/ensure-coverage/references/breadth/surface-discovery.md +3 -3
- package/skills/engineering/ensure-coverage/references/depth/characterization.md +4 -65
- package/skills/engineering/ensure-coverage/references/depth/grading.md +5 -7
- package/skills/engineering/ensure-coverage/references/depth/mock-policy.md +6 -12
- package/skills/engineering/ensure-coverage/references/enforcement/ci-contract.md +1 -1
- package/skills/engineering/ensure-coverage/references/presentation/axis.md +6 -6
- package/skills/engineering/ensure-coverage/references/process/audit-mode.md +7 -23
- package/skills/engineering/ensure-coverage/references/process/output-template.md +18 -133
- package/skills/engineering/ensure-coverage/references/process/test-strategy.md +7 -12
- package/skills/engineering/ensure-coverage/scripts/README.md +12 -4
- package/skills/engineering/golden-path/SKILL.md +1 -1
- package/skills/engineering/harness-runtime/SKILL.md +3 -1
- package/skills/engineering/harness-runtime/bin/workspaces.mjs +52 -0
- package/skills/engineering/harness-runtime/native-workspace.mjs +239 -0
- package/skills/engineering/harness-runtime/references/workspaces.md +78 -0
- package/skills/engineering/harness-runtime/workspace-lifecycle.mjs +2154 -0
- package/skills/engineering/resolve-issues/SKILL.md +40 -61
- package/skills/engineering/resolve-issues/references/ci.md +13 -3
- package/skills/engineering/resolve-issues/references/delivery-progress.md +4 -3
- package/skills/engineering/resolve-issues/references/done.md +11 -2
- package/skills/engineering/resolve-issues/references/expire.md +9 -2
- package/skills/engineering/resolve-issues/references/implementation.md +33 -17
- package/skills/engineering/resolve-issues/references/preflight.md +3 -1
- package/skills/engineering/resolve-issues/references/principles.md +52 -0
- package/skills/engineering/resolve-issues/references/review.md +25 -15
- package/skills/engineering/resolve-issues/references/task-plan.md +64 -27
- package/skills/engineering/resolve-issues/references/workspaces.md +164 -0
- package/skills/engineering/resolve-issues/scripts/cleanup-runs.mjs +185 -317
- package/skills/engineering/resolve-issues/scripts/next-actions.mjs +44 -7
- package/skills/engineering/resolve-issues/scripts/rehydrate.mjs +36 -2
- package/skills/engineering/resolve-issues/scripts/review-verdict.mjs +9 -8
- package/skills/engineering/resolve-issues/scripts/workspace-lifecycle.mjs +1 -0
- package/skills/engineering/resolve-release/SKILL.md +1 -1
- package/skills/engineering/rush-issues/SKILL.md +7 -5
- package/skills/engineering/rush-release/SKILL.md +1 -1
- package/skills/engineering/test-quality/SKILL.md +19 -4
- package/skills/engineering/test-quality/references/lint.md +1 -1
- package/skills/engineering/test-quality/references/principles.md +2 -2
- package/skills/engineering/test-quality/references/testing-conventions.md +1 -1
- package/skills/risk-compliance/compliance-audit/SKILL.md +4 -8
- package/skills/risk-compliance/compliance-audit/references/audience-and-reports.md +12 -0
- package/skills/risk-compliance/contract-compare/SKILL.md +17 -1
- package/skills/risk-compliance/contract-review/SKILL.md +6 -1
- package/skills/risk-compliance/contract-review/references/report-stage.md +17 -0
- package/skills/risk-compliance/doc-authenticity/SKILL.md +11 -1
- package/skills/risk-compliance/fraud-detection/LICENSE +3 -0
- package/skills/risk-compliance/fraud-detection/SKILL.md +148 -0
- package/skills/risk-compliance/{fraud-analysis → fraud-detection}/references/archetypes/first-party-fraud.md +3 -2
- package/skills/risk-compliance/fraud-detection/references/archetypes/third-party-fraud.md +47 -0
- package/skills/risk-compliance/fraud-detection/references/collection-payout-features.md +151 -0
- package/skills/risk-compliance/{fraud-analysis → fraud-detection}/references/contexts/bank-transfer.md +4 -3
- package/skills/risk-compliance/fraud-detection/references/contexts/card-payment.md +70 -0
- package/skills/risk-compliance/fraud-detection/references/contexts/payment-collection.md +34 -0
- package/skills/risk-compliance/fraud-detection/references/contexts/payout.md +34 -0
- package/skills/risk-compliance/fraud-detection/references/enrichment.md +74 -0
- package/skills/risk-compliance/fraud-detection/references/entities.md +48 -0
- package/skills/risk-compliance/{fraud-analysis → fraud-detection}/references/feature-engineering.md +69 -33
- package/skills/risk-compliance/fraud-detection/references/feature-sql.md +150 -0
- package/skills/risk-compliance/{fraud-analysis → fraud-detection}/references/report-rationale.md +4 -2
- package/skills/risk-compliance/{fraud-analysis → fraud-detection}/references/report-template.md +45 -23
- package/skills/risk-compliance/{fraud-analysis → fraud-detection}/references/review-checklist.md +60 -20
- package/skills/risk-compliance/fraud-detection/references/workflow.md +216 -0
- package/skills/risk-compliance/fraud-detection/release-check.mjs +95 -0
- package/skills/risk-compliance/payment-analysis/SKILL.md +36 -17
- package/skills/risk-compliance/payment-analysis/references/auth-rate-actions.md +2 -2
- package/skills/risk-compliance/payment-analysis/references/report-template.md +10 -2
- package/skills/risk-compliance/payment-analysis/references/visualization.md +11 -1
- package/skills/risk-compliance/portfolio-payment-analysis/LICENSE +3 -0
- package/skills/risk-compliance/portfolio-payment-analysis/SKILL.md +170 -0
- package/skills/risk-compliance/portfolio-payment-analysis/references/identity-and-reconciliation.md +46 -0
- package/skills/risk-compliance/portfolio-payment-analysis/references/metrics-and-reporting.md +170 -0
- package/skills/risk-compliance/portfolio-payment-analysis/references/risk-history.md +89 -0
- package/skills/risk-compliance/portfolio-payment-analysis/release-check.mjs +95 -0
- package/skills/risk-compliance/portfolio-payment-analysis/scripts/analyze.py +636 -0
- package/skills/risk-compliance/portfolio-payment-analysis/scripts/build_report.py +386 -0
- package/skills/risk-compliance/portfolio-payment-analysis/scripts/comparison_charts.py +425 -0
- package/skills/risk-compliance/portfolio-payment-analysis/scripts/csv_export.py +52 -0
- package/skills/risk-compliance/portfolio-payment-analysis/scripts/render.py +160 -0
- package/skills/risk-compliance/portfolio-payment-analysis/scripts/risk_history.py +435 -0
- package/skills/risk-compliance/similar-domain/SKILL.md +9 -4
- package/skills/risk-compliance/tld-detection/SKILL.md +124 -239
- package/skills/risk-compliance/tld-detection/agents/openai.yaml +1 -1
- package/skills/risk-compliance/tld-detection/references/clean.md +18 -51
- package/skills/risk-compliance/tld-detection/references/fx-conversion.md +56 -219
- package/skills/risk-compliance/tld-detection/references/metric-contract.md +109 -0
- package/skills/risk-compliance/tld-detection/references/pattern.md +99 -0
- package/skills/risk-compliance/tld-detection/references/report-template.md +49 -272
- package/skills/risk-compliance/tld-detection/scripts/detect_ladders.py +46 -13
- package/skills/risk-compliance/tld-detection/scripts/detect_patterns.py +346 -0
- package/skills/risk-compliance/tld-detection/scripts/detect_streams.py +51 -19
- package/skills/risk-compliance/tld-detection/scripts/profile_groups.py +109 -0
- package/skills/risk-compliance/tld-detection/scripts/render_charts.py +218 -0
- package/skills/risk-compliance/tld-detection/scripts/tld_common.py +243 -0
- package/skills/engineering/contributing/SKILL.md +0 -70
- package/skills/risk-compliance/fraud-analysis/SKILL.md +0 -114
- package/skills/risk-compliance/fraud-analysis/references/archetypes/third-party-fraud.md +0 -27
- package/skills/risk-compliance/fraud-analysis/references/contexts/card-payment.md +0 -30
- package/skills/risk-compliance/fraud-analysis/references/contexts/payment-collection.md +0 -20
- package/skills/risk-compliance/fraud-analysis/references/contexts/payout.md +0 -20
- package/skills/risk-compliance/fraud-analysis/references/workflow.md +0 -175
- package/skills/risk-compliance/tld-detection/references/chinese-polish.md +0 -55
- package/skills/risk-compliance/tld-detection/references/clustering.md +0 -235
- package/skills/risk-compliance/tld-detection/references/decision.md +0 -189
- package/skills/risk-compliance/tld-detection/references/streams.md +0 -141
- package/skills/risk-compliance/tld-detection/references/terminology.md +0 -95
- package/skills/risk-compliance/tld-detection/references/workflow.md +0 -95
- /package/skills/{engineering/contributing → core/write-report}/LICENSE +0 -0
- /package/skills/core/{write-doc → write-report}/references/reports/audit-report-template.md +0 -0
- /package/skills/core/{write-doc → write-report}/references/reports/external-assessment-template.md +0 -0
- /package/skills/core/{write-doc → write-report}/references/reports/management-report-template.md +0 -0
- /package/skills/core/{write-doc → write-report}/references/reports/vars-report-template.md +0 -0
- /package/skills/{engineering/contributing → core/write-report}/release-check.mjs +0 -0
- /package/skills/{risk-compliance/fraud-analysis → engineering/engineering}/LICENSE +0 -0
- /package/skills/engineering/{contributing → engineering}/references/tracker-conventions.md +0 -0
- /package/skills/{risk-compliance/fraud-analysis → engineering/engineering}/release-check.mjs +0 -0
- /package/skills/engineering/{contributing → engineering}/scripts/persist-conventions-core.mjs +0 -0
- /package/skills/risk-compliance/{fraud-analysis → fraud-detection}/references/archetypes/authorized-payment-scam.md +0 -0
- /package/skills/risk-compliance/{fraud-analysis → fraud-detection}/references/mechanisms/account-takeover.md +0 -0
- /package/skills/risk-compliance/{fraud-analysis → fraud-detection}/references/taxonomy.md +0 -0
- /package/skills/risk-compliance/{fraud-analysis → fraud-detection}/references/terminology.md +0 -0
|
@@ -1,364 +1,232 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
//
|
|
2
|
+
// Owned unit workspaces: prepare, clean up, recover, and inventory.
|
|
3
3
|
//
|
|
4
|
-
// cleanup-runs.mjs
|
|
5
|
-
// cleanup-runs.mjs --
|
|
4
|
+
// cleanup-runs.mjs prepare --run <run dir> --unit <id> --revision <n> [--apply] [--json] [--kind worktree|clone]
|
|
5
|
+
// cleanup-runs.mjs cleanup --run <run dir> --unit <id> --revision <n> [--apply] [--json]
|
|
6
|
+
// cleanup-runs.mjs recover --repo <repo root> --json
|
|
7
|
+
// cleanup-runs.mjs inventory --repo <repo root> [--root <dir>] [--max-entries <n>] [--max-depth <n>] --json
|
|
6
8
|
//
|
|
7
|
-
//
|
|
8
|
-
// task-plan.json (the file the orchestrator saves on every transition):
|
|
9
|
+
// Legacy entry points, kept because the coordinator's references still call them:
|
|
9
10
|
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
// idle longer records stay; logs go too
|
|
11
|
+
// cleanup-runs.mjs --repo <repo root> [--apply] [--json]
|
|
12
|
+
// cleanup-runs.mjs --merged <unit-workspace-or-resource> [--apply] [--json]
|
|
13
13
|
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
// .log and logcat files, and text under test-results/, logs/, and similar. Records are the run's own small .json, .jsonl and .md files near the
|
|
20
|
-
// top of the run, outside any clone.
|
|
14
|
+
// --repo is a safe owned completed-unit sweep, never an age-based disk sweep. It only
|
|
15
|
+
// considers units the plan records as succeeded with delivery merged, and then applies the
|
|
16
|
+
// same safety checks as `cleanup`. --merged resolves the saved run and unit that own the
|
|
17
|
+
// path; an unowned path is refused. Neither mode strips ignored files, deletes logs, or
|
|
18
|
+
// removes anything by mtime.
|
|
21
19
|
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
// show as untracked; only its git-ignored paths are swept.
|
|
26
|
-
//
|
|
27
|
-
// --merged handles one unit workspace whose PR has merged: its commits are on
|
|
28
|
-
// the target, so it counts as clean unless it has uncommitted changes or a
|
|
29
|
-
// stash. Its logs stay.
|
|
30
|
-
//
|
|
31
|
-
// Without --apply nothing is touched; the actions are printed. The last line is
|
|
32
|
-
// always a one-line summary. Exit 0 on success, 2 for a bad command.
|
|
20
|
+
// The helper never writes task-plan.json. `prepare` and `cleanup` return the next lifecycle
|
|
21
|
+
// as JSON; the coordinator persists it through update_plan. A successful removal writes a
|
|
22
|
+
// postcondition receipt under <run>/results/<unit>-cleanup.json.
|
|
33
23
|
|
|
34
24
|
import { execFileSync } from 'node:child_process'
|
|
35
|
-
import { existsSync
|
|
36
|
-
import { dirname, join, relative, resolve, sep } from 'node:path'
|
|
25
|
+
import { existsSync } from 'node:fs'
|
|
37
26
|
|
|
38
27
|
import { isMainModule } from '../../engineering-runtime/scripts/main-module.mjs'
|
|
28
|
+
import {
|
|
29
|
+
INVENTORY_MAX_DEPTH, INVENTORY_MAX_ENTRIES, LifecycleError, cleanup, cleanupMerged, inventory, prepare, recover, sweep,
|
|
30
|
+
} from './workspace-lifecycle.mjs'
|
|
39
31
|
|
|
40
|
-
export
|
|
41
|
-
export const DEFAULT_LOG_DAYS = 30
|
|
42
|
-
// Records live near the top of a run: task-plan.json, exploration/, sources/,
|
|
43
|
-
// reviews/<round>/review.json.
|
|
44
|
-
export const RECORD_MAX_DEPTH = 4
|
|
45
|
-
export const RECORD_MAX_BYTES = 1024 * 1024
|
|
46
|
-
export const EVIDENCE_MAX_BYTES = 20 * 1024 * 1024
|
|
47
|
-
const RECORD_EXTENSIONS = ['.json', '.jsonl', '.md']
|
|
48
|
-
const EVIDENCE_EXTENSIONS = ['.log', '.txt', '.json', '.jsonl', '.md', '.out', '.err']
|
|
49
|
-
// Directories that only ever hold downloads or build output.
|
|
50
|
-
export const CACHE_DIRS = new Set([
|
|
51
|
-
'node_modules', '.cache', 'cache', 'caches', 'xdg-cache', '.gradle', '.npm', '.pnpm-store', '.m2', '.android',
|
|
52
|
-
'android-sdk', 'sdk', 'system-images', 'build', 'dist', '.cxx', '.next', '.turbo', 'browsers',
|
|
53
|
-
'playwright-browsers', 'ms-playwright', '.venv', 'venv', '__pycache__', 'DerivedData', 'Pods',
|
|
54
|
-
])
|
|
55
|
-
// A non-Git folder with one of these at its root is a copy of a repository:
|
|
56
|
-
// inside it, only logs and text under an evidence folder count as evidence.
|
|
57
|
-
const LOCKFILES = ['pnpm-lock.yaml', 'package-lock.json', 'yarn.lock', 'bun.lockb', 'uv.lock', 'poetry.lock', 'Cargo.lock', 'go.sum', 'Gemfile.lock']
|
|
58
|
-
const EVIDENCE_DIRS = new Set(['test-results', 'logs', 'log', 'reports', 'artifacts', 'playwright-report'])
|
|
59
|
-
const DAY_MS = 24 * 60 * 60 * 1000
|
|
32
|
+
export { LifecycleError } from './workspace-lifecycle.mjs'
|
|
60
33
|
|
|
61
|
-
|
|
34
|
+
const USAGE = `usage: cleanup-runs.mjs prepare --run <run dir> --unit <id> --revision <n> [--apply] [--json] [--kind worktree|clone]
|
|
35
|
+
cleanup-runs.mjs cleanup --run <run dir> --unit <id> --revision <n> [--apply] [--json]
|
|
36
|
+
cleanup-runs.mjs recover --repo <repo root> --json
|
|
37
|
+
cleanup-runs.mjs inventory --repo <repo root> [--root <dir>] [--max-entries <n>] [--max-depth <n>] --json
|
|
38
|
+
cleanup-runs.mjs --repo <repo root> [--apply] [--json]
|
|
39
|
+
cleanup-runs.mjs --merged <unit-workspace-or-resource> [--apply] [--json]
|
|
62
40
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
41
|
+
exit 0: the command ran and every removal it planned succeeded, or it was a read-only plan
|
|
42
|
+
exit 1: a path was retained/refused for a safety reason, or --apply could not finish a removal
|
|
43
|
+
exit 2: bad arguments, unreadable or contradictory plan, revision mismatch, lock held, or unowned path`
|
|
66
44
|
|
|
67
|
-
|
|
68
|
-
export function workspaceState(dir) {
|
|
69
|
-
try {
|
|
70
|
-
const changed = git(dir, ['status', '--porcelain']).split('\n').filter(Boolean).length
|
|
71
|
-
const unpushed = Number(git(dir, ['rev-list', '--count', 'HEAD', '--branches', '--not', '--remotes']).trim())
|
|
72
|
-
const stashed = git(dir, ['stash', 'list']).split('\n').filter(Boolean).length
|
|
73
|
-
return { changed, unpushed, stashed }
|
|
74
|
-
} catch {
|
|
75
|
-
return null
|
|
76
|
-
}
|
|
77
|
-
}
|
|
45
|
+
const COMMANDS = new Set(['prepare', 'cleanup', 'recover', 'inventory'])
|
|
78
46
|
|
|
79
|
-
|
|
80
|
-
const isCacheDir = (name) => CACHE_DIRS.has(name) || name.endsWith('.avd')
|
|
81
|
-
const isEvidenceName = (name) => EVIDENCE_EXTENSIONS.some((ext) => name.endsWith(ext)) || /logcat/i.test(name)
|
|
82
|
-
const isLogName = (name) => name.endsWith('.log') || /logcat/i.test(name)
|
|
83
|
-
const isSourceCopy = (dir) => LOCKFILES.some((f) => existsSync(join(dir, f)))
|
|
47
|
+
class UsageError extends Error {}
|
|
84
48
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
if (
|
|
88
|
-
return
|
|
49
|
+
function positiveInt(value, flag, { allowZero = false } = {}) {
|
|
50
|
+
const n = Number(value)
|
|
51
|
+
if (!Number.isSafeInteger(n) || n < (allowZero ? 0 : 1)) throw new UsageError(`${flag} must be ${allowZero ? 'a nonnegative' : 'a positive'} integer`)
|
|
52
|
+
return n
|
|
89
53
|
}
|
|
90
54
|
|
|
91
|
-
function
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
return parts.join(', ')
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// What happens to one file: kept as a record, kept as a log, or deleted.
|
|
100
|
-
function fileFate(name, path, ctx) {
|
|
101
|
-
if (ctx.tracked?.has(path)) return 'delete'
|
|
102
|
-
const size = statSync(path).size
|
|
103
|
-
const isRecord = !ctx.inClone && !ctx.hidden && ctx.depth <= RECORD_MAX_DEPTH
|
|
104
|
-
&& RECORD_EXTENSIONS.some((ext) => name.endsWith(ext)) && size <= RECORD_MAX_BYTES
|
|
105
|
-
if (isRecord) return 'record'
|
|
106
|
-
const evidence = ctx.sourceCopy && !ctx.inEvidenceDir ? isLogName(name) : isEvidenceName(name)
|
|
107
|
-
if (ctx.keepLogs && evidence && size <= EVIDENCE_MAX_BYTES) return 'log'
|
|
108
|
-
return 'delete'
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Actions for one directory. Returns { actions, kept, logs }; a directory with
|
|
112
|
-
// nothing kept collapses into a single delete.
|
|
113
|
-
function sweep(dir, ctx) {
|
|
114
|
-
const actions = []
|
|
115
|
-
let kept = false
|
|
116
|
-
let logs = 0
|
|
117
|
-
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
118
|
-
const path = join(dir, entry.name)
|
|
119
|
-
if (entry.isDirectory()) {
|
|
120
|
-
if (isCacheDir(entry.name) || (ctx.inClone && entry.name === '.git')) {
|
|
121
|
-
actions.push({ kind: 'delete', path })
|
|
122
|
-
continue
|
|
123
|
-
}
|
|
124
|
-
if (!ctx.inClone && isGitWorkspace(path)) {
|
|
125
|
-
const action = workspaceAction(path, { keepLogs: ctx.keepLogs, depth: ctx.depth + 1 })
|
|
126
|
-
actions.push(action)
|
|
127
|
-
logs += action.logs ?? 0
|
|
128
|
-
if (action.kind !== 'delete') kept = true
|
|
129
|
-
continue
|
|
130
|
-
}
|
|
131
|
-
const sub = sweep(path, {
|
|
132
|
-
...ctx,
|
|
133
|
-
depth: ctx.depth + 1,
|
|
134
|
-
hidden: ctx.hidden || entry.name.startsWith('.'),
|
|
135
|
-
...enterDir(ctx, entry.name, path),
|
|
136
|
-
})
|
|
137
|
-
if (sub.kept) {
|
|
138
|
-
actions.push(...sub.actions)
|
|
139
|
-
kept = true
|
|
140
|
-
logs += sub.logs
|
|
141
|
-
} else {
|
|
142
|
-
actions.push({ kind: 'delete', path })
|
|
143
|
-
}
|
|
144
|
-
} else if (entry.isFile()) {
|
|
145
|
-
const fate = fileFate(entry.name, path, ctx)
|
|
146
|
-
if (fate === 'delete') actions.push({ kind: 'delete', path })
|
|
147
|
-
else {
|
|
148
|
-
kept = true
|
|
149
|
-
if (fate === 'log') logs++
|
|
150
|
-
}
|
|
151
|
-
} else {
|
|
152
|
-
actions.push({ kind: 'delete', path })
|
|
153
|
-
}
|
|
55
|
+
export function parseArgs(argv) {
|
|
56
|
+
const opts = {
|
|
57
|
+
command: null, run: null, unit: null, revision: null, repo: null, merged: null,
|
|
58
|
+
apply: false, json: false, help: false, kind: null, root: null,
|
|
59
|
+
maxEntries: INVENTORY_MAX_ENTRIES, maxDepth: INVENTORY_MAX_DEPTH,
|
|
154
60
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
const ctx = { depth, hidden: false, inClone: true, keepLogs }
|
|
165
|
-
if (!unsaved.changed && !unsaved.unpushed && !unsaved.stashed) {
|
|
166
|
-
const tracked = new Set(zList(git(dir, ['ls-files', '-z'])).map((p) => join(dir, p)))
|
|
167
|
-
const sub = sweep(dir, { ...ctx, tracked })
|
|
168
|
-
const reason = merged ? 'merged and clean' : 'clean and pushed'
|
|
169
|
-
if (!sub.kept) return { kind: 'delete', path: dir, reason, deletes: [], logs: 0 }
|
|
170
|
-
return { kind: 'sweep', path: dir, reason: `${reason}; keeps ${sub.logs} log file(s)`, deletes: sub.actions, logs: sub.logs }
|
|
171
|
-
}
|
|
172
|
-
// Only git-ignored paths are fair game; tracked and untracked files are work.
|
|
173
|
-
const deletes = []
|
|
174
|
-
let logs = 0
|
|
175
|
-
for (const rel of zList(git(dir, ['ls-files', '--others', '--ignored', '--exclude-standard', '--directory', '-z']))) {
|
|
176
|
-
const path = join(dir, rel)
|
|
177
|
-
if (!existsSync(path)) continue
|
|
178
|
-
const name = rel.replace(/\/$/, '').split('/').pop()
|
|
179
|
-
if (lstatSync(path).isDirectory()) {
|
|
180
|
-
if (isCacheDir(name)) { deletes.push({ kind: 'delete', path }); continue }
|
|
181
|
-
const sub = sweep(path, { ...ctx, depth: depth + rel.split('/').length, ...enterDir({ ...ctx, sourceCopy: false, inEvidenceDir: false }, name, path) })
|
|
182
|
-
if (sub.kept) { deletes.push(...sub.actions); logs += sub.logs } else deletes.push({ kind: 'delete', path })
|
|
183
|
-
} else if (fileFate(name, path, ctx) === 'delete') {
|
|
184
|
-
deletes.push({ kind: 'delete', path })
|
|
185
|
-
} else {
|
|
186
|
-
logs++
|
|
61
|
+
for (let i = 0; i < argv.length; i++) {
|
|
62
|
+
const arg = argv[i]
|
|
63
|
+
if (COMMANDS.has(arg) && !opts.command) { opts.command = arg; continue }
|
|
64
|
+
if (arg === '--apply') { opts.apply = true; continue }
|
|
65
|
+
if (arg === '--json') { opts.json = true; continue }
|
|
66
|
+
if (arg === '--help' || arg === '-h') { opts.help = true; continue }
|
|
67
|
+
const value = () => {
|
|
68
|
+
if (argv[i + 1] === undefined || argv[i + 1].startsWith('--')) throw new UsageError(`${arg} needs a value`)
|
|
69
|
+
return argv[++i]
|
|
187
70
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
71
|
+
if (arg === '--run') opts.run = value()
|
|
72
|
+
else if (arg === '--unit') opts.unit = value()
|
|
73
|
+
else if (arg === '--revision') opts.revision = positiveInt(value(), '--revision', { allowZero: true })
|
|
74
|
+
else if (arg === '--repo') opts.repo = value()
|
|
75
|
+
else if (arg === '--merged') opts.merged = value()
|
|
76
|
+
else if (arg === '--kind') opts.kind = value()
|
|
77
|
+
else if (arg === '--root') opts.root = value()
|
|
78
|
+
else if (arg === '--max-entries') opts.maxEntries = positiveInt(value(), '--max-entries')
|
|
79
|
+
else if (arg === '--max-depth') opts.maxDepth = positiveInt(value(), '--max-depth', { allowZero: true })
|
|
80
|
+
else if (arg === '--older-than-days' || arg === '--log-days') {
|
|
81
|
+
throw new UsageError(`${arg} was removed: age no longer authorizes a deletion. A workspace is cleaned only from task-plan ownership, completion, and the safety checks. Run inventory to see what is on disk`)
|
|
82
|
+
} else throw new UsageError(`unrecognized argument ${arg}`)
|
|
83
|
+
}
|
|
84
|
+
if (!opts.command && !opts.merged && !opts.repo) throw new UsageError('no command, --repo, or --merged given')
|
|
85
|
+
if (opts.command === 'prepare' || opts.command === 'cleanup') {
|
|
86
|
+
if (!opts.run) throw new UsageError(`${opts.command} needs --run`)
|
|
87
|
+
if (!opts.unit) throw new UsageError(`${opts.command} needs --unit`)
|
|
88
|
+
if (opts.revision === null) throw new UsageError(`${opts.command} needs --revision`)
|
|
89
|
+
if (opts.merged) throw new UsageError(`${opts.command} does not take --merged`)
|
|
90
|
+
}
|
|
91
|
+
if ((opts.command === 'recover' || opts.command === 'inventory') && !opts.repo) throw new UsageError(`${opts.command} needs --repo`)
|
|
92
|
+
if (opts.command === 'recover' && opts.apply) throw new UsageError('recover is read-only and does not take --apply')
|
|
93
|
+
if (opts.command === 'inventory' && opts.apply) throw new UsageError('inventory is read-only and does not take --apply')
|
|
94
|
+
if (opts.command === 'prepare' && opts.root) throw new UsageError('prepare does not take --root')
|
|
95
|
+
if (opts.command && opts.merged) throw new UsageError(`${opts.command} does not take --merged`)
|
|
96
|
+
return opts
|
|
195
97
|
}
|
|
196
98
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
const runs = []
|
|
201
|
-
for (const entry of readdirSync(root, { withFileTypes: true })) {
|
|
202
|
-
if (!entry.isDirectory()) continue
|
|
203
|
-
const dir = join(root, entry.name)
|
|
204
|
-
const idleDays = (now - lastActive(dir)) / DAY_MS
|
|
205
|
-
if (idleDays < olderThanDays) {
|
|
206
|
-
runs.push({ run: entry.name, dir, idleDays, stage: 'active', actions: [], logs: 0 })
|
|
207
|
-
continue
|
|
208
|
-
}
|
|
209
|
-
const keepLogs = idleDays < logDays
|
|
210
|
-
const result = sweep(dir, { depth: 1, hidden: false, inClone: false, keepLogs })
|
|
211
|
-
runs.push({ run: entry.name, dir, idleDays, stage: keepLogs ? 'keep-logs' : 'records-only', actions: result.actions, logs: result.logs })
|
|
212
|
-
}
|
|
213
|
-
runs.sort((a, b) => a.run.localeCompare(b.run))
|
|
214
|
-
return { root, runs }
|
|
99
|
+
function ghAvailableRunner() {
|
|
100
|
+
if (process.env.CLEANUP_RUNS_NO_GH === '1') return null
|
|
101
|
+
return (args) => execFileSync('gh', args, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], timeout: 30000, maxBuffer: 4 * 1024 * 1024 })
|
|
215
102
|
}
|
|
216
103
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
if (!dir.split(sep).includes('.resolve-issues')) {
|
|
220
|
-
throw new CleanupError(`--merged only removes workspaces under a .resolve-issues directory: ${dir}`)
|
|
221
|
-
}
|
|
222
|
-
if (!existsSync(dir) || !lstatSync(dir).isDirectory() || !isGitWorkspace(dir)) {
|
|
223
|
-
throw new CleanupError(`not a Git workspace: ${dir}`)
|
|
224
|
-
}
|
|
225
|
-
return workspaceAction(dir, { merged: true, keepLogs: true })
|
|
104
|
+
function printJson(value) {
|
|
105
|
+
process.stdout.write(`${JSON.stringify(value, null, 2)}\n`)
|
|
226
106
|
}
|
|
227
107
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
108
|
+
function failure(command, error) {
|
|
109
|
+
const code = error instanceof LifecycleError || error instanceof UsageError ? error.code ?? 'usage' : 'error'
|
|
110
|
+
return {
|
|
111
|
+
version: 1,
|
|
112
|
+
command,
|
|
113
|
+
ok: false,
|
|
114
|
+
applied: false,
|
|
115
|
+
action: 'refuse',
|
|
116
|
+
reason: error.message,
|
|
117
|
+
code,
|
|
118
|
+
error: { code, message: error.message, ...(error.details ? { details: error.details } : {}) },
|
|
119
|
+
errors: [error.message],
|
|
233
120
|
}
|
|
234
|
-
if (readdirSync(dir).length === 0) rmdirSync(dir)
|
|
235
121
|
}
|
|
236
122
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
try {
|
|
246
|
-
execFileSync('chmod', ['u+w', dirname(path)], { stdio: 'ignore' })
|
|
247
|
-
execFileSync('chmod', ['-R', 'u+w', path], { stdio: 'ignore' })
|
|
248
|
-
rmSync(path, { recursive: true, force: true })
|
|
249
|
-
return null
|
|
250
|
-
} catch (error) {
|
|
251
|
-
return `${path}: ${error.code ?? error.message}`
|
|
252
|
-
}
|
|
253
|
-
}
|
|
123
|
+
function renderPrepare(result) {
|
|
124
|
+
const lines = []
|
|
125
|
+
const dry = result.applied ? '' : ' (dry run)'
|
|
126
|
+
lines.push(`prepare${dry}: ${result.action} ${result.workspace.kind} ${result.workspace.path}`)
|
|
127
|
+
lines.push(` base ${result.workspace.baseSha} revision ${result.revision} unit ${result.unit}`)
|
|
128
|
+
if (result.resources.length) lines.push(` resources: ${result.resources.map((r) => `${r.role}${r.path}`).join(', ')}`)
|
|
129
|
+
if (!result.applied) lines.push(' nothing was created; rerun with --apply')
|
|
130
|
+
return lines.join('\n')
|
|
254
131
|
}
|
|
255
132
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
const
|
|
259
|
-
const
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
}
|
|
263
|
-
if (
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
if (action.kind === 'sweep' && failures.length === 0) pruneEmpty(action.path)
|
|
267
|
-
}
|
|
268
|
-
return failures
|
|
133
|
+
function renderCleanup(result) {
|
|
134
|
+
const lines = []
|
|
135
|
+
const verb = result.applied ? '' : ' (dry run)'
|
|
136
|
+
const target = result.workspace?.path ?? result.paths?.[0] ?? ''
|
|
137
|
+
lines.push(`${result.command}${verb}: ${result.action} ${target}`)
|
|
138
|
+
if (result.reason) lines.push(` ${result.reason}`)
|
|
139
|
+
for (const check of result.checks ?? []) lines.push(` ${check.ok ? 'ok ' : 'FAIL'} ${check.name}: ${check.detail}`)
|
|
140
|
+
if (result.receipt) lines.push(` receipt ${result.receipt}`)
|
|
141
|
+
if (result.wouldRemove) lines.push(' nothing was removed; rerun with --apply')
|
|
142
|
+
return lines.join('\n')
|
|
269
143
|
}
|
|
270
144
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
const
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
return null
|
|
145
|
+
function renderSweep(result) {
|
|
146
|
+
const lines = [`sweep${result.applied ? '' : ' (dry run)'}: ${result.repo}`]
|
|
147
|
+
for (const run of result.runs) {
|
|
148
|
+
lines.push(` run ${run.run}${run.revision !== null ? ` (revision ${run.revision})` : ''}`)
|
|
149
|
+
for (const unit of run.units) {
|
|
150
|
+
lines.push(` ${unit.unit}: ${unit.action}${unit.reason ? ` — ${unit.reason}` : ''}`)
|
|
151
|
+
}
|
|
279
152
|
}
|
|
153
|
+
if (result.unknownEntries.length) lines.push(` unknown entries (inventory only, never removed): ${result.unknownEntries.join(', ')}`)
|
|
154
|
+
const s = result.summary
|
|
155
|
+
lines.push(` ${s.runs} run(s), ${s.completed} completed unit(s): ${s.removed} removed, ${s.retained} retained, ${s.refused} refused; ${s.unknown} unknown entr${s.unknown === 1 ? 'y' : 'ies'}`)
|
|
156
|
+
return lines.join('\n')
|
|
280
157
|
}
|
|
281
158
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
const opts = { repo: process.cwd(), olderThanDays: DEFAULT_OLDER_THAN_DAYS, logDays: DEFAULT_LOG_DAYS, apply: false, merged: null }
|
|
287
|
-
const days = (value) => {
|
|
288
|
-
const n = Number(value)
|
|
289
|
-
if (!Number.isFinite(n) || n < 0) throw new CleanupError(usage)
|
|
290
|
-
return n
|
|
291
|
-
}
|
|
292
|
-
for (let i = 0; i < argv.length; i++) {
|
|
293
|
-
const arg = argv[i]
|
|
294
|
-
if (arg === '--apply') opts.apply = true
|
|
295
|
-
else if (arg === '--repo' && argv[i + 1]) opts.repo = argv[++i]
|
|
296
|
-
else if (arg === '--merged' && argv[i + 1]) opts.merged = argv[++i]
|
|
297
|
-
else if (arg === '--older-than-days' && argv[i + 1]) opts.olderThanDays = days(argv[++i])
|
|
298
|
-
else if (arg === '--log-days' && argv[i + 1]) opts.logDays = days(argv[++i])
|
|
299
|
-
else throw new CleanupError(usage)
|
|
159
|
+
function renderRecover(result) {
|
|
160
|
+
const lines = [`recover (read-only): ${result.repo}`]
|
|
161
|
+
for (const run of result.runs) {
|
|
162
|
+
for (const unit of run.units) lines.push(` ${unit.unit}: ${unit.action}${unit.reason ? ` — ${unit.reason}` : ''}`)
|
|
300
163
|
}
|
|
301
|
-
|
|
302
|
-
return
|
|
164
|
+
lines.push(` ${result.due.length} due operation(s), ${result.worktrees.length} registered worktree(s), ${result.locks.length} lock(s)`)
|
|
165
|
+
return lines.join('\n')
|
|
303
166
|
}
|
|
304
167
|
|
|
305
|
-
function
|
|
306
|
-
const
|
|
307
|
-
|
|
168
|
+
function renderInventory(result) {
|
|
169
|
+
const lines = [`inventory (read-only): ${result.root}`]
|
|
170
|
+
lines.push(` ${result.entries.length} entr${result.entries.length === 1 ? 'y' : 'ies'}${result.truncated ? ' (truncated)' : ''}; ${result.runs.length} run(s); ${result.registeredWorktrees.length} registered worktree(s)`)
|
|
171
|
+
if (result.reason) lines.push(` ${result.reason}`)
|
|
172
|
+
return lines.join('\n')
|
|
308
173
|
}
|
|
309
174
|
|
|
310
|
-
function
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
for (const r of plan.runs) {
|
|
325
|
-
const idle = `idle ${r.idleDays.toFixed(1)} days`
|
|
326
|
-
if (r.stage === 'active') {
|
|
327
|
-
out(`run ${r.run} (${idle}): skipped, used within ${opts.olderThanDays} days`)
|
|
328
|
-
continue
|
|
329
|
-
}
|
|
330
|
-
const note = r.stage === 'records-only' ? `records only, past ${opts.logDays} days` : `keeps records and ${r.logs} log file(s)`
|
|
331
|
-
out(`run ${r.run} (${idle}): ${note}${r.actions.length ? '' : '; nothing to remove'}`)
|
|
332
|
-
for (const a of r.actions) out(` ${describeAction(a, r.dir)}`)
|
|
333
|
-
actions.push(...r.actions)
|
|
334
|
-
logs += r.logs
|
|
175
|
+
export function main(argv) {
|
|
176
|
+
let opts
|
|
177
|
+
let command = argv.find((arg) => COMMANDS.has(arg)) ?? (argv.includes('--merged') ? 'merged' : 'sweep')
|
|
178
|
+
try {
|
|
179
|
+
opts = parseArgs(argv)
|
|
180
|
+
if (opts.help) { process.stdout.write(`${USAGE}\n`); return 0 }
|
|
181
|
+
command = opts.command ?? (opts.merged ? 'merged' : 'sweep')
|
|
182
|
+
const runGh = ghAvailableRunner()
|
|
183
|
+
|
|
184
|
+
if (command === 'prepare') {
|
|
185
|
+
const result = prepare({ run: opts.run, unit: opts.unit, revision: opts.revision, apply: opts.apply, kind: opts.kind })
|
|
186
|
+
if (opts.json) printJson(result)
|
|
187
|
+
else process.stdout.write(`${renderPrepare(result)}\n`)
|
|
188
|
+
return 0
|
|
335
189
|
}
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
190
|
+
if (command === 'cleanup') {
|
|
191
|
+
const result = cleanup({ run: opts.run, unit: opts.unit, revision: opts.revision, apply: opts.apply, requireApplying: true, runGh })
|
|
192
|
+
if (opts.json) printJson(result)
|
|
193
|
+
else process.stdout.write(`${renderCleanup(result)}\n`)
|
|
194
|
+
return result.ok ? 0 : (result.action === 'refuse' ? 2 : 1)
|
|
195
|
+
}
|
|
196
|
+
if (command === 'recover') {
|
|
197
|
+
const result = recover({ repo: opts.repo, runGh })
|
|
198
|
+
if (opts.json) printJson(result)
|
|
199
|
+
else process.stdout.write(`${renderRecover(result)}\n`)
|
|
200
|
+
return result.ok ? 0 : 2
|
|
201
|
+
}
|
|
202
|
+
if (command === 'inventory') {
|
|
203
|
+
const result = inventory({ repo: opts.repo, root: opts.root, maxEntries: opts.maxEntries, maxDepth: opts.maxDepth })
|
|
204
|
+
if (opts.json) printJson(result)
|
|
205
|
+
else process.stdout.write(`${renderInventory(result)}\n`)
|
|
206
|
+
return 0
|
|
207
|
+
}
|
|
208
|
+
if (command === 'merged') {
|
|
209
|
+
const result = cleanupMerged({ repo: opts.repo, path: opts.merged, apply: opts.apply, requireApplying: false, runGh: ghAvailableRunner() })
|
|
210
|
+
if (opts.json) printJson(result)
|
|
211
|
+
else process.stdout.write(`${renderCleanup(result)}\n`)
|
|
212
|
+
return result.ok ? 0 : 1
|
|
213
|
+
}
|
|
214
|
+
if (command === 'sweep') {
|
|
215
|
+
const result = sweep({ repo: opts.repo, apply: opts.apply, runGh })
|
|
216
|
+
if (opts.json) printJson(result)
|
|
217
|
+
else process.stdout.write(`${renderSweep(result)}\n`)
|
|
218
|
+
return result.ok ? 0 : 2
|
|
219
|
+
}
|
|
220
|
+
throw new UsageError(USAGE)
|
|
221
|
+
} catch (error) {
|
|
222
|
+
if (opts?.json) printJson(failure(command, error))
|
|
223
|
+
else process.stderr.write(`${error instanceof UsageError ? `${error.message}\n${USAGE}` : `${error.message}\n`}`)
|
|
224
|
+
if (error instanceof UsageError) return 2
|
|
225
|
+
if (error instanceof LifecycleError) return 2
|
|
226
|
+
return 2
|
|
350
227
|
}
|
|
351
|
-
const after = freeBytes(base)
|
|
352
|
-
const freed = before !== null && after !== null ? `; freed ${gigabytes(Math.max(0, after - before))}` : ''
|
|
353
|
-
const failed = failures.length ? `; ${failures.length} path(s) could not be removed` : ''
|
|
354
|
-
out(`cleanup-runs: ${summary}${freed}${failed}`)
|
|
355
228
|
}
|
|
356
229
|
|
|
357
230
|
if (isMainModule(import.meta.url)) {
|
|
358
|
-
|
|
359
|
-
run(parseArgs(process.argv.slice(2)), (line) => process.stdout.write(`${line}\n`))
|
|
360
|
-
} catch (error) {
|
|
361
|
-
process.stderr.write(`${error instanceof CleanupError ? error.message : error.stack}\n`)
|
|
362
|
-
process.exitCode = 2
|
|
363
|
-
}
|
|
231
|
+
process.exitCode = main(process.argv.slice(2))
|
|
364
232
|
}
|