akm-cli 0.9.0-beta.9 → 0.9.0-rc.1
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/CHANGELOG.md +715 -0
- package/README.md +12 -4
- package/dist/akm +38 -0
- package/dist/akm-migrate-storage +38 -0
- package/dist/assets/help/help-improve.md +9 -6
- package/dist/assets/hints/cli-hints-full.md +6 -5
- package/dist/assets/profiles/default.json +9 -4
- package/dist/assets/profiles/frequent.json +1 -1
- package/dist/assets/profiles/memory-focus.json +1 -1
- package/dist/assets/profiles/proactive-maintenance.json +25 -0
- package/dist/assets/profiles/quick.json +1 -1
- package/dist/assets/profiles/recombine-only.json +21 -0
- package/dist/assets/profiles/reflect-distill.json +30 -0
- package/dist/assets/profiles/synthesize.json +15 -0
- package/dist/assets/profiles/thorough.json +1 -1
- package/dist/assets/prompts/consolidate-system.md +23 -0
- package/dist/assets/prompts/contradiction-judge.md +33 -0
- package/dist/assets/prompts/distill-knowledge-system.md +22 -0
- package/dist/assets/prompts/distill-lesson-system.md +36 -0
- package/dist/assets/prompts/extract-session.md +11 -3
- package/dist/assets/prompts/graph-extract-system.md +1 -0
- package/dist/assets/prompts/graph-extract-user-prompt.md +1 -1
- package/dist/assets/prompts/memory-infer-system.md +1 -0
- package/dist/assets/prompts/memory-infer-user.md +5 -0
- package/dist/assets/prompts/metadata-enhance-system.md +1 -0
- package/dist/assets/prompts/procedural-system.md +44 -0
- package/dist/assets/prompts/recombine-system.md +40 -0
- package/dist/assets/prompts/staleness-detect-system.md +6 -0
- package/dist/assets/prompts/validate-summary-judge.md +1 -0
- package/dist/assets/prompts/workflow-unit-preamble.md +26 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +38 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +38 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +39 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +40 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +43 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +38 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +43 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +40 -0
- package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +43 -0
- package/dist/assets/templates/html/health.html +281 -111
- package/dist/assets/wiki/ingest-workflow-template.md +45 -16
- package/dist/assets/wiki/schema-template.md +4 -4
- package/dist/cli/clack.js +56 -0
- package/dist/cli/config-migrate.js +7 -1
- package/dist/cli/confirm.js +1 -1
- package/dist/cli/parse-args.js +46 -1
- package/dist/cli/shared.js +28 -0
- package/dist/cli.js +25 -21
- package/dist/commands/agent/agent-dispatch.js +3 -2
- package/dist/commands/agent/agent-support.js +0 -7
- package/dist/commands/agent/contribute-cli.js +26 -7
- package/dist/commands/config-cli.js +26 -13
- package/dist/commands/env/child-env.js +47 -0
- package/dist/commands/env/env-binding.js +95 -0
- package/dist/commands/env/env-cli.js +228 -292
- package/dist/commands/env/env.js +14 -67
- package/dist/commands/env/secret-cli.js +140 -138
- package/dist/commands/feedback-cli.js +156 -155
- package/dist/commands/graph/graph-cli.js +5 -13
- package/dist/commands/graph/graph.js +3 -3
- package/dist/commands/health/advisories.js +151 -0
- package/dist/commands/health/checks.js +103 -16
- package/dist/commands/health/html-report.js +447 -81
- package/dist/commands/health/improve-metrics.js +771 -0
- package/dist/commands/health/llm-usage.js +65 -0
- package/dist/commands/health/md-report.js +103 -0
- package/dist/commands/health/metrics.js +278 -0
- package/dist/commands/health/stash-exposure.js +46 -0
- package/dist/commands/health/surfaces.js +216 -0
- package/dist/commands/health/task-runs.js +135 -0
- package/dist/commands/health/types.js +26 -0
- package/dist/commands/health/windows.js +195 -0
- package/dist/commands/health.js +91 -1091
- package/dist/commands/improve/anti-collapse.js +170 -0
- package/dist/commands/improve/calibration.js +161 -0
- package/dist/commands/improve/collapse-detector.js +421 -0
- package/dist/commands/improve/consolidate/chunking.js +141 -0
- package/dist/commands/improve/consolidate/eligibility.js +64 -0
- package/dist/commands/improve/consolidate/merge.js +145 -0
- package/dist/commands/improve/consolidate/sanitize.js +231 -0
- package/dist/commands/{lint.js → improve/consolidate/types.js} +1 -1
- package/dist/commands/improve/consolidate.js +1295 -1277
- package/dist/commands/improve/dedup.js +482 -0
- package/dist/commands/improve/distill/content-repair.js +202 -0
- package/dist/commands/improve/distill/promote-memory.js +229 -0
- package/dist/commands/improve/distill/quality-gate.js +236 -0
- package/dist/commands/improve/distill-guards.js +127 -0
- package/dist/commands/improve/distill-promotion-policy.js +826 -167
- package/dist/commands/improve/distill.js +228 -605
- package/dist/commands/improve/eligibility.js +434 -0
- package/dist/commands/improve/encoding-salience.js +205 -0
- package/dist/commands/improve/extract-cli.js +179 -59
- package/dist/commands/improve/extract-prompt.js +54 -3
- package/dist/commands/improve/extract-watch.js +140 -0
- package/dist/commands/improve/extract.js +409 -43
- package/dist/commands/improve/feedback-valence.js +54 -0
- package/dist/commands/improve/hot-probation.js +45 -0
- package/dist/commands/improve/improve-auto-accept.js +157 -10
- package/dist/commands/improve/improve-cli.js +115 -73
- package/dist/commands/improve/improve-profiles.js +28 -8
- package/dist/commands/improve/improve-result-file.js +15 -25
- package/dist/commands/improve/improve-session.js +58 -0
- package/dist/commands/improve/improve.js +485 -2764
- package/dist/commands/improve/locks.js +154 -0
- package/dist/commands/improve/loop-stages.js +1100 -0
- package/dist/commands/improve/memory/memory-belief.js +14 -15
- package/dist/commands/improve/memory/memory-contradiction-detect.js +83 -60
- package/dist/commands/improve/memory/memory-improve.js +27 -27
- package/dist/commands/improve/outcome-loop.js +270 -0
- package/dist/commands/improve/preparation.js +2002 -0
- package/dist/commands/improve/proactive-maintenance.js +37 -35
- package/dist/commands/improve/procedural.js +398 -0
- package/dist/commands/improve/recombine.js +818 -0
- package/dist/commands/improve/reflect-noise.js +0 -0
- package/dist/commands/improve/reflect.js +206 -45
- package/dist/commands/improve/salience.js +455 -0
- package/dist/commands/improve/schema-similarity-gate.js +168 -0
- package/dist/commands/improve/shared.js +51 -0
- package/dist/commands/improve/triage.js +93 -0
- package/dist/commands/lint/agent-linter.js +19 -24
- package/dist/commands/lint/base-linter.js +173 -60
- package/dist/commands/lint/command-linter.js +19 -24
- package/dist/commands/lint/env-key-rules.js +38 -1
- package/dist/commands/lint/fact-linter.js +39 -0
- package/dist/commands/lint/index.js +31 -13
- package/dist/commands/lint/memory-linter.js +1 -1
- package/dist/commands/lint/registry.js +7 -2
- package/dist/commands/lint/task-linter.js +3 -3
- package/dist/commands/lint/workflow-linter.js +26 -1
- package/dist/commands/observability-cli.js +4 -4
- package/dist/commands/proposal/drain-policies.js +13 -4
- package/dist/commands/proposal/drain.js +45 -51
- package/dist/commands/proposal/legacy-import.js +115 -0
- package/dist/commands/proposal/proposal-cli.js +24 -34
- package/dist/commands/proposal/proposal.js +2 -1
- package/dist/commands/proposal/propose.js +8 -3
- package/dist/commands/proposal/repository.js +829 -0
- package/dist/commands/proposal/validators/proposal-quality-validators.js +9 -8
- package/dist/commands/proposal/validators/proposals.js +93 -895
- package/dist/commands/read/curate.js +410 -111
- package/dist/commands/read/knowledge.js +10 -3
- package/dist/commands/read/remember-cli.js +133 -138
- package/dist/commands/read/search-cli.js +15 -8
- package/dist/commands/read/search.js +22 -11
- package/dist/commands/read/show.js +106 -14
- package/dist/commands/registry-cli.js +76 -87
- package/dist/commands/remember.js +11 -12
- package/dist/commands/sources/add-cli.js +91 -95
- package/dist/commands/sources/history.js +1 -1
- package/dist/commands/sources/init.js +66 -18
- package/dist/commands/sources/installed-stashes.js +11 -3
- package/dist/commands/sources/migration-help.js +7 -4
- package/dist/commands/sources/schema-repair.js +44 -46
- package/dist/commands/sources/self-update.js +2 -2
- package/dist/commands/sources/source-add.js +7 -3
- package/dist/commands/sources/sources-cli.js +3 -3
- package/dist/commands/sources/stash-cli.js +19 -39
- package/dist/commands/sources/stash-skeleton.js +57 -8
- package/dist/commands/tasks/default-tasks.js +15 -2
- package/dist/commands/tasks/tasks-cli.js +20 -29
- package/dist/commands/tasks/tasks.js +39 -11
- package/dist/commands/wiki-cli.js +23 -38
- package/dist/commands/workflow-cli.js +291 -13
- package/dist/core/asset/asset-registry.js +3 -1
- package/dist/core/asset/asset-spec.js +79 -5
- package/dist/core/asset/frontmatter.js +188 -167
- package/dist/core/asset/markdown.js +8 -0
- package/dist/core/authoring-rules.js +92 -0
- package/dist/core/common.js +4 -23
- package/dist/core/concurrent.js +10 -1
- package/dist/core/config/config-io.js +10 -1
- package/dist/core/config/config-migration.js +18 -40
- package/dist/core/config/config-schema.js +403 -62
- package/dist/core/config/config-types.js +3 -3
- package/dist/core/config/config.js +67 -22
- package/dist/core/deep-merge.js +38 -0
- package/dist/core/errors.js +1 -0
- package/dist/core/eval/rank-metrics.js +113 -0
- package/dist/core/events.js +4 -7
- package/dist/core/improve-types.js +47 -8
- package/dist/core/json-schema.js +142 -0
- package/dist/core/logs-db.js +14 -75
- package/dist/core/parse.js +36 -16
- package/dist/core/paths.js +18 -18
- package/dist/core/standards/resolve-standards-context.js +87 -0
- package/dist/core/standards/resolve-stash-standards.js +99 -0
- package/dist/core/standards/resolve-type-conventions.js +66 -0
- package/dist/core/state/migrations.js +770 -0
- package/dist/core/state-db.js +132 -1126
- package/dist/core/structured.js +69 -0
- package/dist/core/time.js +53 -0
- package/dist/core/warn.js +21 -0
- package/dist/core/write-source.js +37 -0
- package/dist/indexer/db/db.js +261 -770
- package/dist/indexer/db/entry-mapper.js +41 -0
- package/dist/indexer/db/graph-db.js +129 -86
- package/dist/indexer/db/llm-cache.js +2 -2
- package/dist/indexer/db/schema.js +516 -0
- package/dist/indexer/ensure-index.js +36 -92
- package/dist/indexer/feedback/utility-policy.js +75 -0
- package/dist/indexer/graph/graph-boost.js +51 -41
- package/dist/indexer/graph/graph-extraction.js +207 -4
- package/dist/indexer/index-writer-lock.js +18 -11
- package/dist/indexer/index-written-assets.js +105 -0
- package/dist/indexer/indexer.js +182 -204
- package/dist/indexer/passes/dir-staleness.js +114 -0
- package/dist/indexer/passes/memory-inference.js +13 -5
- package/dist/indexer/passes/metadata.js +20 -0
- package/dist/indexer/read-preflight.js +23 -0
- package/dist/indexer/search/db-search.js +89 -13
- package/dist/indexer/search/fts-query.js +51 -0
- package/dist/indexer/search/ranking-contributors.js +95 -9
- package/dist/indexer/search/ranking.js +79 -3
- package/dist/indexer/search/search-fields.js +6 -0
- package/dist/indexer/search/search-source.js +32 -21
- package/dist/indexer/search/semantic-status.js +4 -0
- package/dist/indexer/walk/matchers.js +48 -0
- package/dist/indexer/walk/walker.js +21 -13
- package/dist/integrations/agent/builders.js +41 -13
- package/dist/integrations/agent/config.js +20 -59
- package/dist/integrations/agent/detect.js +9 -0
- package/dist/integrations/agent/index.js +3 -19
- package/dist/integrations/agent/model-aliases.js +16 -2
- package/dist/integrations/agent/profiles.js +79 -6
- package/dist/integrations/agent/prompts.js +75 -9
- package/dist/integrations/agent/runner-dispatch.js +83 -0
- package/dist/integrations/agent/runner.js +13 -9
- package/dist/integrations/agent/spawn.js +206 -81
- package/dist/integrations/harnesses/aider/agent-builder.js +113 -0
- package/dist/integrations/harnesses/aider/index.js +58 -0
- package/dist/integrations/harnesses/aider/result-extractor.js +53 -0
- package/dist/integrations/harnesses/amazonq/agent-builder.js +153 -0
- package/dist/integrations/harnesses/amazonq/index.js +59 -0
- package/dist/integrations/harnesses/amazonq/result-extractor.js +48 -0
- package/dist/integrations/harnesses/claude/agent-builder.js +46 -7
- package/dist/integrations/harnesses/claude/index.js +27 -23
- package/dist/integrations/harnesses/claude/result-extractor.js +52 -0
- package/dist/integrations/harnesses/claude/session-log.js +10 -0
- package/dist/integrations/harnesses/codex/agent-builder.js +137 -0
- package/dist/integrations/harnesses/codex/index.js +63 -0
- package/dist/integrations/harnesses/codex/result-extractor.js +73 -0
- package/dist/integrations/harnesses/copilot/agent-builder.js +122 -0
- package/dist/integrations/harnesses/copilot/index.js +60 -0
- package/dist/integrations/harnesses/copilot/result-extractor.js +151 -0
- package/dist/integrations/harnesses/gemini/agent-builder.js +121 -0
- package/dist/integrations/harnesses/gemini/index.js +60 -0
- package/dist/integrations/harnesses/gemini/result-extractor.js +121 -0
- package/dist/integrations/harnesses/index.js +28 -7
- package/dist/integrations/harnesses/opencode/agent-builder.js +1 -1
- package/dist/integrations/harnesses/opencode/index.js +17 -16
- package/dist/integrations/harnesses/opencode/session-log.js +173 -3
- package/dist/integrations/harnesses/opencode-sdk/harness.js +65 -0
- package/dist/integrations/harnesses/opencode-sdk/index.js +10 -34
- package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +642 -71
- package/dist/integrations/harnesses/openhands/agent-builder.js +126 -0
- package/dist/integrations/harnesses/openhands/index.js +58 -0
- package/dist/integrations/harnesses/openhands/result-extractor.js +103 -0
- package/dist/integrations/harnesses/pi/agent-builder.js +104 -0
- package/dist/integrations/harnesses/pi/index.js +58 -0
- package/dist/integrations/harnesses/pi/result-extractor.js +135 -0
- package/dist/integrations/harnesses/types.js +8 -0
- package/dist/integrations/session-logs/index.js +40 -11
- package/dist/llm/call-ai.js +2 -2
- package/dist/llm/client.js +34 -11
- package/dist/llm/embedder.js +67 -4
- package/dist/llm/embedders/cache.js +3 -1
- package/dist/llm/embedders/deterministic.js +66 -0
- package/dist/llm/embedders/local.js +73 -3
- package/dist/llm/feature-gate.js +16 -15
- package/dist/llm/graph-extract.js +67 -44
- package/dist/llm/memory-infer-impl.js +138 -0
- package/dist/llm/memory-infer.js +1 -127
- package/dist/llm/metadata-enhance.js +44 -31
- package/dist/llm/structured-call.js +49 -0
- package/dist/migrate-storage-node.mjs +8 -0
- package/dist/output/context.js +5 -5
- package/dist/output/renderers.js +87 -15
- package/dist/output/shapes/curate.js +14 -2
- package/dist/output/shapes/helpers.js +0 -3
- package/dist/output/shapes/passthrough.js +6 -1
- package/dist/output/text/helpers.js +241 -2
- package/dist/output/text/workflow.js +4 -1
- package/dist/registry/providers/skills-sh.js +21 -147
- package/dist/registry/providers/static-index.js +15 -157
- package/dist/registry/resolve.js +27 -9
- package/dist/runtime.js +25 -1
- package/dist/schemas/akm-config.json +14225 -0
- package/dist/schemas/akm-workflow.json +328 -0
- package/dist/scripts/migrate-storage.js +2743 -8390
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +1652 -607
- package/dist/setup/detect.js +9 -0
- package/dist/setup/legacy-config.js +106 -0
- package/dist/setup/prompt.js +57 -0
- package/dist/setup/providers.js +14 -0
- package/dist/setup/registry-stash-loader.js +12 -0
- package/dist/setup/semantic-assets.js +124 -0
- package/dist/setup/setup.js +52 -1614
- package/dist/setup/steps/connection.js +734 -0
- package/dist/setup/steps/output.js +31 -0
- package/dist/setup/steps/platforms.js +124 -0
- package/dist/setup/steps/semantic.js +27 -0
- package/dist/setup/steps/sources.js +222 -0
- package/dist/setup/steps/stashdir.js +42 -0
- package/dist/setup/steps/tasks.js +152 -0
- package/dist/sources/include.js +6 -2
- package/dist/sources/providers/filesystem.js +0 -1
- package/dist/sources/providers/git-install.js +210 -0
- package/dist/sources/providers/git-provider.js +234 -0
- package/dist/sources/providers/git-stash.js +248 -0
- package/dist/sources/providers/git.js +10 -661
- package/dist/sources/providers/npm.js +2 -6
- package/dist/sources/providers/provider-utils.js +13 -7
- package/dist/sources/providers/sync-from-ref.js +9 -1
- package/dist/sources/providers/website.js +9 -5
- package/dist/sources/website-ingest.js +187 -29
- package/dist/sources/wiki-fetchers/registry.js +53 -0
- package/dist/sources/wiki-fetchers/youtube.js +239 -0
- package/dist/storage/database.js +45 -10
- package/dist/storage/managed-db.js +82 -0
- package/dist/storage/repositories/canaries-repository.js +107 -0
- package/dist/storage/repositories/consolidation-repository.js +38 -0
- package/dist/storage/repositories/embeddings-repository.js +72 -0
- package/dist/storage/repositories/events-repository.js +187 -0
- package/dist/storage/repositories/extract-sessions-repository.js +96 -0
- package/dist/storage/repositories/improve-runs-repository.js +146 -0
- package/dist/storage/repositories/index-db.js +14 -8
- package/dist/storage/repositories/proposals-repository.js +220 -0
- package/dist/storage/repositories/recombine-repository.js +213 -0
- package/dist/storage/repositories/registry-cache.js +93 -0
- package/dist/storage/repositories/registry-index-cache-repository.js +46 -0
- package/dist/storage/repositories/task-history-repository.js +93 -0
- package/dist/storage/repositories/workflow-runs-repository.js +189 -1
- package/dist/storage/sqlite-pragmas.js +146 -0
- package/dist/tasks/backends/cron.js +1 -1
- package/dist/tasks/backends/index.js +9 -0
- package/dist/tasks/backends/launchd.js +1 -1
- package/dist/tasks/backends/schtasks.js +1 -1
- package/dist/tasks/{resolveAkmBin.js → resolve-akm-bin.js} +2 -2
- package/dist/tasks/runner.js +15 -13
- package/dist/text-import-hook.mjs +1 -1
- package/dist/wiki/wiki.js +52 -11
- package/dist/workflows/authoring/authoring.js +123 -10
- package/dist/workflows/authoring/workflow-program-template.yaml +31 -0
- package/dist/workflows/cli.js +5 -0
- package/dist/workflows/db.js +138 -4
- package/dist/workflows/exec/brief.js +484 -0
- package/dist/workflows/exec/native-executor.js +975 -0
- package/dist/workflows/exec/param-secrets.js +115 -0
- package/dist/workflows/exec/report.js +1295 -0
- package/dist/workflows/exec/run-workflow.js +596 -0
- package/dist/workflows/exec/scheduler.js +100 -0
- package/dist/workflows/exec/step-work.js +1156 -0
- package/dist/workflows/exec/unit-writer.js +23 -0
- package/dist/workflows/exec/watch.js +116 -0
- package/dist/workflows/exec/worktree.js +171 -0
- package/dist/workflows/ir/compile.js +388 -0
- package/dist/workflows/ir/params.js +54 -0
- package/dist/workflows/ir/plan-hash.js +33 -0
- package/dist/workflows/ir/schema.js +4 -0
- package/dist/workflows/parser.js +3 -1
- package/dist/workflows/program/expressions.js +369 -0
- package/dist/workflows/program/parser.js +760 -0
- package/dist/workflows/program/project.js +105 -0
- package/dist/workflows/program/schema.js +54 -0
- package/dist/workflows/renderer.js +82 -5
- package/dist/workflows/runtime/agent-identity.js +59 -14
- package/dist/workflows/runtime/runs.js +248 -153
- package/dist/workflows/runtime/unit-checkin.js +45 -0
- package/dist/workflows/runtime/workflow-asset-loader.js +188 -0
- package/dist/workflows/validate-summary.js +26 -10
- package/dist/workflows/validator.js +1 -1
- package/docs/README.md +69 -18
- package/docs/data-and-telemetry.md +7 -5
- package/docs/migration/release-notes/0.7.0.md +1 -1
- package/docs/migration/release-notes/0.9.0-beta.60.md +19 -0
- package/docs/migration/release-notes/0.9.0.md +39 -0
- package/package.json +10 -10
- package/dist/assets/tasks/core/update-stashes.yml +0 -4
- package/dist/commands/db-cli.js +0 -23
- package/dist/indexer/db/db-backup.js +0 -376
- package/dist/indexer/passes/staleness-detect.js +0 -488
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { TYPE_DIRS } from "../../core/asset/asset-spec.js";
|
|
7
|
+
import { resolveStashDir } from "../../core/common.js";
|
|
8
|
+
import { getSources, loadConfig } from "../../core/config/config.js";
|
|
9
|
+
import { UsageError } from "../../core/errors.js";
|
|
10
|
+
import { sanitizeCommitMessage } from "../../core/write-source.js";
|
|
11
|
+
import { runGit } from "./git-install.js";
|
|
12
|
+
import { getCachePaths, parseGitRepoUrl } from "./git-provider.js";
|
|
13
|
+
/**
|
|
14
|
+
* Recognize a stash directory as git-backed by the presence of a `.git` entry.
|
|
15
|
+
*
|
|
16
|
+
* Recognition is deliberately by `.git` presence — NOT by a configured remote.
|
|
17
|
+
* `akm init` git-inits the primary stash (see init.ts `ensureGitRepo`), so a
|
|
18
|
+
* freshly-initialized local stash with no remote is still git-backed. This is
|
|
19
|
+
* the single source of truth used both by `saveGitStash` (below) and by the
|
|
20
|
+
* end-of-run improve auto-sync gate.
|
|
21
|
+
*/
|
|
22
|
+
export function isGitBackedStash(stashDir) {
|
|
23
|
+
return fs.existsSync(path.join(stashDir, ".git"));
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Resolve the writable-override flag for an end-of-run / `akm sync` commit on
|
|
27
|
+
* the primary stash. Returns `true` when the root config explicitly marks the
|
|
28
|
+
* primary stash writable, otherwise `undefined` (leave the per-stash default
|
|
29
|
+
* untouched). Extracted so `akm sync`, `akm improve`'s end-of-run sync, and the
|
|
30
|
+
* CLI body all derive this identically instead of re-copying the expression.
|
|
31
|
+
*/
|
|
32
|
+
export function resolveWritableOverride(config) {
|
|
33
|
+
return config.writable === true ? true : undefined;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Commit (and optionally push) local changes in a git-backed stash.
|
|
37
|
+
*
|
|
38
|
+
* Behaviour:
|
|
39
|
+
* - Not a git repo → skipped (no-op)
|
|
40
|
+
* - Git repo, no remote → commit only
|
|
41
|
+
* - Git repo, has remote, but stash is not writable → commit only
|
|
42
|
+
* - Git repo, has remote, stash is writable → commit + push
|
|
43
|
+
*
|
|
44
|
+
* When `name` is omitted the primary stash directory is used.
|
|
45
|
+
* When `message` is omitted a timestamp is used.
|
|
46
|
+
*
|
|
47
|
+
* `options.repoDir` overrides the primary-stash directory the commit targets
|
|
48
|
+
* (only honoured when `name` is omitted). Callers that already resolved the
|
|
49
|
+
* primary stash dir (e.g. `akm improve`'s end-of-run sync, whose pre-commit
|
|
50
|
+
* gate validates that exact directory) pass it here so the gate and the commit
|
|
51
|
+
* operate on the SAME directory instead of independently calling
|
|
52
|
+
* `resolveStashDir({ readOnly: true })`. When absent, behaviour is unchanged.
|
|
53
|
+
*/
|
|
54
|
+
export function saveGitStash(name, message, writableOverride, options) {
|
|
55
|
+
// `push: false` (from `akm sync --no-push`) commits but never pushes, even
|
|
56
|
+
// when the stash is writable with a remote configured.
|
|
57
|
+
const allowPush = options?.push !== false;
|
|
58
|
+
const timestamp = new Date().toISOString().replace("T", " ").slice(0, 19);
|
|
59
|
+
// Sanitize the user-supplied message: strip CR/LF/NUL, collapse whitespace,
|
|
60
|
+
// clamp length. An attacker can otherwise pass `--message "subject\n\n\
|
|
61
|
+
// Co-Authored-By: someone-else"` and forge trailers in the commit log.
|
|
62
|
+
// Empty result falls back to the timestamped default.
|
|
63
|
+
const sanitized = message ? sanitizeCommitMessage(message) : "";
|
|
64
|
+
const commitMessage = sanitized || `akm save ${timestamp}`;
|
|
65
|
+
let repoDir;
|
|
66
|
+
let writable = false;
|
|
67
|
+
if (name) {
|
|
68
|
+
const config = loadConfig();
|
|
69
|
+
const stash = findGitStashByTarget(getSources(config), name);
|
|
70
|
+
if (!stash)
|
|
71
|
+
throw new UsageError(`No git stash found with name "${name}"`);
|
|
72
|
+
if (stash.type !== "git") {
|
|
73
|
+
throw new UsageError(`Stash "${name}" is not a git stash (type: ${stash.type})`);
|
|
74
|
+
}
|
|
75
|
+
if (!stash.url)
|
|
76
|
+
throw new UsageError(`Stash "${name}" has no URL configured`);
|
|
77
|
+
const repo = parseGitRepoUrl(stash.url);
|
|
78
|
+
repoDir = getCachePaths(repo.canonicalUrl).repoDir;
|
|
79
|
+
writable = stash.writable === true;
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
// Honour an explicit primary-stash dir override (keeps the improve gate and
|
|
83
|
+
// the commit on the same directory); otherwise resolve the default.
|
|
84
|
+
repoDir = options?.repoDir ?? resolveStashDir({ readOnly: true });
|
|
85
|
+
// Allow caller to override writable for the primary stash (e.g. from root config.writable)
|
|
86
|
+
if (writableOverride !== undefined) {
|
|
87
|
+
writable = writableOverride;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// No-op: not a git repo
|
|
91
|
+
if (!isGitBackedStash(repoDir)) {
|
|
92
|
+
return { committed: false, pushed: false, skipped: true, reason: "not a git repository", output: "" };
|
|
93
|
+
}
|
|
94
|
+
// Nothing to commit?
|
|
95
|
+
const statusResult = runGit(["-C", repoDir, "status", "--porcelain"]);
|
|
96
|
+
if (statusResult.error || statusResult.status !== 0) {
|
|
97
|
+
throw new Error(`git status failed: ${statusResult.error?.message || statusResult.stderr?.trim() || "unknown error"}`);
|
|
98
|
+
}
|
|
99
|
+
if (!statusResult.stdout.trim()) {
|
|
100
|
+
return { committed: false, pushed: false, skipped: false, output: "nothing to commit, working tree clean" };
|
|
101
|
+
}
|
|
102
|
+
// Scoped staging (#476 + the auto-sync incident): NEVER refuse akm's commit
|
|
103
|
+
// because unrelated non-akm files exist in the working tree. When the stash
|
|
104
|
+
// dir is shared with a non-akm project (stash root == project repo root), a
|
|
105
|
+
// blunt `git add -A` would sweep the user's unrelated WIP into the stash's
|
|
106
|
+
// remote. We avoid that by SCOPING what we stage, not by refusing the commit.
|
|
107
|
+
//
|
|
108
|
+
// Precedence:
|
|
109
|
+
// 1. Explicit modified-file list (`options.paths`) — stage exactly those.
|
|
110
|
+
// 2. Managed pathspecs (TYPE_DIRS values + `.akm`) that exist on disk —
|
|
111
|
+
// stages everything akm owns and, by construction, never stages non-akm
|
|
112
|
+
// WIP. This preserves the #476 protection WITHOUT refusing.
|
|
113
|
+
// 3. Last-resort `git add -A` — ONLY when neither an explicit list nor any
|
|
114
|
+
// managed pathspec can be resolved. This is the maintainer-approved
|
|
115
|
+
// "or all files if we cannot determine the exact file list" fallback and
|
|
116
|
+
// is the one (rare) path that could include unrelated non-akm files.
|
|
117
|
+
if (!stageScopedChanges(repoDir, options?.paths)) {
|
|
118
|
+
throw new Error(`git add failed while staging akm changes in ${repoDir}`);
|
|
119
|
+
}
|
|
120
|
+
// Nothing actually staged → don't create an empty commit. This happens when
|
|
121
|
+
// only non-akm files were dirty (precedence 2 staged nothing).
|
|
122
|
+
const stagedResult = runGit(["-C", repoDir, "diff", "--cached", "--quiet"]);
|
|
123
|
+
if (stagedResult.status === 0) {
|
|
124
|
+
return { committed: false, pushed: false, skipped: false, output: "nothing to commit" };
|
|
125
|
+
}
|
|
126
|
+
// Commit — supply fallback identity so fresh environments without
|
|
127
|
+
// user.name/user.email configured can always commit to the default stash.
|
|
128
|
+
const commitResult = runGit([
|
|
129
|
+
"-C",
|
|
130
|
+
repoDir,
|
|
131
|
+
"-c",
|
|
132
|
+
"user.name=akm",
|
|
133
|
+
"-c",
|
|
134
|
+
"user.email=akm@local",
|
|
135
|
+
"commit",
|
|
136
|
+
"-m",
|
|
137
|
+
commitMessage,
|
|
138
|
+
]);
|
|
139
|
+
if (commitResult.status !== 0) {
|
|
140
|
+
throw new Error(`git commit failed: ${commitResult.stderr?.trim() || "unknown error"}`);
|
|
141
|
+
}
|
|
142
|
+
// Push only when there is a remote AND the stash is marked writable
|
|
143
|
+
const remoteResult = runGit(["-C", repoDir, "remote"]);
|
|
144
|
+
if (remoteResult.status !== 0) {
|
|
145
|
+
throw new Error(`git remote failed: ${remoteResult.stderr?.trim() || "unknown error"}`);
|
|
146
|
+
}
|
|
147
|
+
const hasRemote = remoteResult.stdout.trim().length > 0;
|
|
148
|
+
if (!hasRemote || !writable || !allowPush) {
|
|
149
|
+
return { committed: true, pushed: false, skipped: false, output: commitResult.stdout.trim() };
|
|
150
|
+
}
|
|
151
|
+
const pushResult = runGit(["-C", repoDir, "push"], { timeout: 120_000 });
|
|
152
|
+
if (pushResult.status !== 0) {
|
|
153
|
+
throw new Error(`git push failed: ${pushResult.stderr?.trim() || "unknown error"}`);
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
committed: true,
|
|
157
|
+
pushed: true,
|
|
158
|
+
skipped: false,
|
|
159
|
+
output: (commitResult.stdout + pushResult.stdout).trim() || "changes committed and pushed",
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Stage akm's changes in `repoDir` using the scoped-staging precedence
|
|
164
|
+
* documented at the call site (#476). Returns `false` only when a `git add`
|
|
165
|
+
* subprocess fails; returns `true` otherwise (including when nothing matched —
|
|
166
|
+
* the caller then detects "nothing staged" via `git diff --cached --quiet`).
|
|
167
|
+
*
|
|
168
|
+
* @param paths Optional explicit repo-relative paths akm wrote this run. When
|
|
169
|
+
* provided and non-empty, exactly those are staged (chunked to stay under
|
|
170
|
+
* argv length limits). Otherwise we fall back to the managed pathspecs, and
|
|
171
|
+
* finally to `git add -A` only if no managed pathspec exists on disk.
|
|
172
|
+
*/
|
|
173
|
+
function stageScopedChanges(repoDir, paths) {
|
|
174
|
+
// Precedence 1: explicit modified-file list.
|
|
175
|
+
const explicit = (paths ?? []).filter((p) => typeof p === "string" && p.length > 0);
|
|
176
|
+
if (explicit.length > 0) {
|
|
177
|
+
return addPathspecsChunked(repoDir, explicit);
|
|
178
|
+
}
|
|
179
|
+
// Precedence 2: managed pathspecs that exist on disk (TYPE_DIRS + `.akm`).
|
|
180
|
+
const managed = [...Object.values(TYPE_DIRS), ".akm"].filter((dir) => fs.existsSync(path.join(repoDir, dir)));
|
|
181
|
+
if (managed.length > 0) {
|
|
182
|
+
return addPathspecsChunked(repoDir, managed);
|
|
183
|
+
}
|
|
184
|
+
// Precedence 3 (last resort): nothing akm-managed resolved on disk. Stage
|
|
185
|
+
// everything. This is the ONLY branch that can include unrelated non-akm
|
|
186
|
+
// files — it is the explicit, maintainer-approved "or all files if we cannot
|
|
187
|
+
// determine the exact file list" fallback and should be rare/never in
|
|
188
|
+
// practice (a git-backed stash always has at least one managed subtree once
|
|
189
|
+
// akm has written to it).
|
|
190
|
+
const addAll = runGit(["-C", repoDir, "add", "-A"]);
|
|
191
|
+
return addAll.status === 0;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Run `git add -- <pathspec>...` in chunks so a very large path list never
|
|
195
|
+
* exceeds the OS argv-length limit. Each chunk must succeed.
|
|
196
|
+
*/
|
|
197
|
+
function addPathspecsChunked(repoDir, pathspecs) {
|
|
198
|
+
const CHUNK = 500;
|
|
199
|
+
for (let i = 0; i < pathspecs.length; i += CHUNK) {
|
|
200
|
+
const chunk = pathspecs.slice(i, i + CHUNK);
|
|
201
|
+
const result = runGit(["-C", repoDir, "add", "--", ...chunk]);
|
|
202
|
+
if (result.status !== 0)
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
return true;
|
|
206
|
+
}
|
|
207
|
+
function findGitStashByTarget(stashes, target) {
|
|
208
|
+
return stashes.find((stash) => matchesGitStashTarget(stash, target));
|
|
209
|
+
}
|
|
210
|
+
function matchesGitStashTarget(stash, target) {
|
|
211
|
+
if (stash.type !== "git")
|
|
212
|
+
return false;
|
|
213
|
+
if (stash.name === target || stash.url === target)
|
|
214
|
+
return true;
|
|
215
|
+
if (!stash.url)
|
|
216
|
+
return false;
|
|
217
|
+
try {
|
|
218
|
+
const repo = parseGitRepoUrl(stash.url);
|
|
219
|
+
if (repo.canonicalUrl === target)
|
|
220
|
+
return true;
|
|
221
|
+
return buildGithubTargetAliases(repo.canonicalUrl).has(target);
|
|
222
|
+
}
|
|
223
|
+
catch {
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
function buildGithubTargetAliases(canonicalUrl) {
|
|
228
|
+
try {
|
|
229
|
+
const parsed = new URL(canonicalUrl);
|
|
230
|
+
if (parsed.hostname !== "github.com")
|
|
231
|
+
return new Set();
|
|
232
|
+
const segments = parsed.pathname.split("/").filter(Boolean);
|
|
233
|
+
if (segments.length < 2)
|
|
234
|
+
return new Set();
|
|
235
|
+
const owner = segments[0];
|
|
236
|
+
const repo = segments[1];
|
|
237
|
+
const aliases = new Set([`${owner}/${repo}`, `github:${owner}/${repo}`]);
|
|
238
|
+
if (segments[2] === "tree" && segments.length >= 4) {
|
|
239
|
+
const ref = segments.slice(3).join("/");
|
|
240
|
+
aliases.add(`${owner}/${repo}#${ref}`);
|
|
241
|
+
aliases.add(`github:${owner}/${repo}#${ref}`);
|
|
242
|
+
}
|
|
243
|
+
return aliases;
|
|
244
|
+
}
|
|
245
|
+
catch {
|
|
246
|
+
return new Set();
|
|
247
|
+
}
|
|
248
|
+
}
|