akm-cli 0.7.4 → 0.8.0-rc.10
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 +224 -1
- package/README.md +22 -6
- package/SECURITY.md +93 -0
- package/dist/cli/config-migrate.js +144 -0
- package/dist/cli/config-validate.js +39 -0
- package/dist/cli/confirm.js +73 -0
- package/dist/cli/parse-args.js +133 -0
- package/dist/cli/shared.js +129 -0
- package/dist/cli.js +2631 -1440
- package/dist/commands/add-cli.js +279 -0
- package/dist/commands/agent-dispatch.js +110 -0
- package/dist/commands/agent-support.js +68 -0
- package/dist/commands/completions.js +3 -0
- package/dist/commands/config-cli.js +130 -534
- package/dist/commands/consolidate.js +2122 -0
- package/dist/commands/curate.js +45 -3
- package/dist/commands/db-cli.js +23 -0
- package/dist/commands/distill-promotion-policy.js +660 -0
- package/dist/commands/distill.js +1081 -73
- package/dist/commands/env.js +213 -0
- package/dist/commands/eval-cases.js +43 -0
- package/dist/commands/events.js +15 -24
- package/dist/commands/extract-cli.js +127 -0
- package/dist/commands/extract-prompt.js +204 -0
- package/dist/commands/extract.js +477 -0
- package/dist/commands/feedback-cli.js +331 -0
- package/dist/commands/graph.js +477 -0
- package/dist/commands/health.js +1302 -0
- package/dist/commands/help/help-accept.md +12 -0
- package/dist/commands/help/help-improve.md +69 -0
- package/dist/commands/help/help-proposals.md +18 -0
- package/dist/commands/help/help-propose.md +17 -0
- package/dist/commands/help/help-reject.md +11 -0
- package/dist/commands/history.js +54 -46
- package/dist/commands/improve-auto-accept.js +97 -0
- package/dist/commands/improve-cli.js +217 -0
- package/dist/commands/improve-profiles.js +166 -0
- package/dist/commands/improve-result-file.js +167 -0
- package/dist/commands/improve.js +2373 -0
- package/dist/commands/info.js +5 -2
- package/dist/commands/init.js +50 -2
- package/dist/commands/installed-stashes.js +102 -139
- package/dist/commands/knowledge.js +136 -0
- package/dist/commands/lint/agent-linter.js +49 -0
- package/dist/commands/lint/base-linter.js +479 -0
- package/dist/commands/lint/command-linter.js +49 -0
- package/dist/commands/lint/default-linter.js +16 -0
- package/dist/commands/lint/env-key-rules.js +154 -0
- package/dist/commands/lint/index.js +196 -0
- package/dist/commands/lint/knowledge-linter.js +16 -0
- package/dist/commands/lint/markdown-insertion.js +343 -0
- package/dist/commands/lint/memory-linter.js +61 -0
- package/dist/commands/lint/registry.js +36 -0
- package/dist/commands/lint/skill-linter.js +45 -0
- package/dist/commands/lint/task-linter.js +50 -0
- package/dist/commands/lint/types.js +4 -0
- package/dist/commands/lint/workflow-linter.js +56 -0
- package/dist/commands/lint.js +4 -0
- package/dist/commands/migration-help.js +3 -0
- package/dist/commands/proposal.js +67 -12
- package/dist/commands/propose.js +120 -45
- package/dist/commands/reflect.js +1104 -60
- package/dist/commands/registry-cli.js +150 -0
- package/dist/commands/registry-search.js +5 -2
- package/dist/commands/remember-cli.js +257 -0
- package/dist/commands/remember.js +70 -7
- package/dist/commands/schema-repair.js +203 -0
- package/dist/commands/search.js +115 -14
- package/dist/commands/secret.js +173 -0
- package/dist/commands/self-update.js +3 -0
- package/dist/commands/show.js +158 -60
- package/dist/commands/source-add.js +17 -45
- package/dist/commands/source-clone.js +3 -0
- package/dist/commands/source-manage.js +14 -19
- package/dist/commands/tasks.js +437 -0
- package/dist/commands/url-checker.js +42 -0
- package/dist/core/action-contributors.js +28 -0
- package/dist/core/asset-ref.js +17 -2
- package/dist/core/asset-registry.js +12 -17
- package/dist/core/asset-serialize.js +88 -0
- package/dist/core/asset-spec.js +67 -1
- package/dist/core/common.js +182 -0
- package/dist/core/concurrent.js +25 -0
- package/dist/core/config-io.js +347 -0
- package/dist/core/config-migration.js +622 -0
- package/dist/core/config-schema.js +534 -0
- package/dist/core/config-sources.js +108 -0
- package/dist/core/config-types.js +4 -0
- package/dist/core/config-walker.js +337 -0
- package/dist/core/config.js +364 -968
- package/dist/core/errors.js +42 -20
- package/dist/core/events.js +105 -135
- package/dist/core/file-lock.js +104 -0
- package/dist/core/frontmatter.js +75 -8
- package/dist/core/lesson-lint.js +3 -0
- package/dist/core/markdown.js +20 -0
- package/dist/core/memory-belief.js +62 -0
- package/dist/core/memory-contradiction-detect.js +274 -0
- package/dist/core/memory-improve.js +806 -0
- package/dist/core/parse.js +158 -0
- package/dist/core/paths.js +280 -14
- package/dist/core/proposal-quality-validators.js +380 -0
- package/dist/core/proposal-validators.js +69 -0
- package/dist/core/proposals.js +512 -42
- package/dist/core/state-db.js +1068 -0
- package/dist/core/text-truncation.js +107 -0
- package/dist/core/time.js +54 -0
- package/dist/core/tty.js +59 -0
- package/dist/core/warn.js +64 -1
- package/dist/core/write-source.js +3 -0
- package/dist/indexer/db-backup.js +391 -0
- package/dist/indexer/db-search.js +198 -489
- package/dist/indexer/db.js +990 -108
- package/dist/indexer/ensure-index.js +136 -0
- package/dist/indexer/file-context.js +3 -0
- package/dist/indexer/graph-boost.js +376 -101
- package/dist/indexer/graph-db.js +391 -0
- package/dist/indexer/graph-dedup.js +95 -0
- package/dist/indexer/graph-extraction.js +550 -114
- package/dist/indexer/index-context.js +4 -0
- package/dist/indexer/indexer.js +547 -309
- package/dist/indexer/llm-cache.js +52 -0
- package/dist/indexer/manifest.js +3 -0
- package/dist/indexer/matchers.js +167 -160
- package/dist/indexer/memory-inference.js +152 -74
- package/dist/indexer/metadata-contributors.js +29 -0
- package/dist/indexer/metadata.js +275 -196
- package/dist/indexer/path-resolver.js +92 -0
- package/dist/indexer/project-context.js +192 -0
- package/dist/indexer/ranking-contributors.js +331 -0
- package/dist/indexer/ranking.js +81 -0
- package/dist/indexer/search-fields.js +5 -9
- package/dist/indexer/search-hit-enrichers.js +111 -0
- package/dist/indexer/search-source.js +44 -10
- package/dist/indexer/semantic-status.js +6 -17
- package/dist/indexer/staleness-detect.js +447 -0
- package/dist/indexer/usage-events.js +12 -9
- package/dist/indexer/walker.js +28 -0
- package/dist/integrations/agent/builders.js +135 -0
- package/dist/integrations/agent/config.js +122 -230
- package/dist/integrations/agent/detect.js +3 -0
- package/dist/integrations/agent/index.js +7 -13
- package/dist/integrations/agent/model-aliases.js +55 -0
- package/dist/integrations/agent/profiles.js +70 -5
- package/dist/integrations/agent/prompts.js +250 -36
- package/dist/integrations/agent/runner.js +151 -0
- package/dist/integrations/agent/sdk-runner.js +126 -0
- package/dist/integrations/agent/spawn.js +183 -35
- package/dist/integrations/github.js +3 -0
- package/dist/integrations/lockfile.js +32 -69
- package/dist/integrations/session-logs/index.js +69 -0
- package/dist/integrations/session-logs/inline-refs.js +35 -0
- package/dist/integrations/session-logs/pre-filter.js +152 -0
- package/dist/integrations/session-logs/providers/claude-code.js +282 -0
- package/dist/integrations/session-logs/providers/opencode.js +258 -0
- package/dist/integrations/session-logs/types.js +4 -0
- package/dist/llm/call-ai.js +62 -0
- package/dist/llm/client.js +79 -88
- package/dist/llm/embedder.js +20 -29
- package/dist/llm/embedders/cache.js +3 -7
- package/dist/llm/embedders/local.js +42 -1
- package/dist/llm/embedders/remote.js +20 -8
- package/dist/llm/embedders/types.js +3 -7
- package/dist/llm/feature-gate.js +95 -48
- package/dist/llm/graph-extract.js +676 -72
- package/dist/llm/index-passes.js +44 -29
- package/dist/llm/memory-infer.js +80 -71
- package/dist/llm/metadata-enhance.js +42 -29
- package/dist/llm/prompts/extract-session.md +80 -0
- package/dist/llm/prompts/graph-extract-user-prompt.md +35 -0
- package/dist/output/cli-hints-full.md +292 -0
- package/dist/output/cli-hints-short.md +66 -0
- package/dist/output/cli-hints.js +7 -311
- package/dist/output/context.js +60 -8
- package/dist/output/renderers.js +306 -258
- package/dist/output/shapes/curate.js +56 -0
- package/dist/output/shapes/distill.js +10 -0
- package/dist/output/shapes/env-list.js +19 -0
- package/dist/output/shapes/events.js +11 -0
- package/dist/output/shapes/helpers.js +424 -0
- package/dist/output/shapes/history.js +7 -0
- package/dist/output/shapes/passthrough.js +102 -0
- package/dist/output/shapes/proposal-accept.js +7 -0
- package/dist/output/shapes/proposal-diff.js +7 -0
- package/dist/output/shapes/proposal-list.js +7 -0
- package/dist/output/shapes/proposal-producer.js +11 -0
- package/dist/output/shapes/proposal-reject.js +7 -0
- package/dist/output/shapes/proposal-show.js +7 -0
- package/dist/output/shapes/registry-search.js +6 -0
- package/dist/output/shapes/registry.js +30 -0
- package/dist/output/shapes/search.js +6 -0
- package/dist/output/shapes/secret-list.js +19 -0
- package/dist/output/shapes/show.js +6 -0
- package/dist/output/shapes/vault-list.js +19 -0
- package/dist/output/shapes.js +51 -511
- package/dist/output/text/add.js +6 -0
- package/dist/output/text/clone.js +6 -0
- package/dist/output/text/config.js +6 -0
- package/dist/output/text/curate.js +6 -0
- package/dist/output/text/distill.js +7 -0
- package/dist/output/text/enable-disable.js +7 -0
- package/dist/output/text/events.js +10 -0
- package/dist/output/text/feedback.js +6 -0
- package/dist/output/text/helpers.js +1039 -0
- package/dist/output/text/history.js +7 -0
- package/dist/output/text/import.js +6 -0
- package/dist/output/text/index.js +6 -0
- package/dist/output/text/info.js +6 -0
- package/dist/output/text/init.js +6 -0
- package/dist/output/text/list.js +6 -0
- package/dist/output/text/proposal-producer.js +8 -0
- package/dist/output/text/proposal.js +11 -0
- package/dist/output/text/registry-commands.js +11 -0
- package/dist/output/text/registry.js +30 -0
- package/dist/output/text/remember.js +6 -0
- package/dist/output/text/remove.js +6 -0
- package/dist/output/text/save.js +6 -0
- package/dist/output/text/search.js +6 -0
- package/dist/output/text/show.js +6 -0
- package/dist/output/text/update.js +6 -0
- package/dist/output/text/upgrade.js +6 -0
- package/dist/output/text/vault.js +16 -0
- package/dist/output/text/wiki.js +15 -0
- package/dist/output/text/workflow.js +14 -0
- package/dist/output/text.js +44 -1093
- package/dist/registry/build-index.js +3 -0
- package/dist/registry/create-provider-registry.js +3 -0
- package/dist/registry/factory.js +4 -1
- package/dist/registry/origin-resolve.js +3 -0
- package/dist/registry/providers/index.js +3 -0
- package/dist/registry/providers/skills-sh.js +71 -50
- package/dist/registry/providers/static-index.js +53 -48
- package/dist/registry/providers/types.js +3 -24
- package/dist/registry/resolve.js +11 -16
- package/dist/registry/types.js +3 -0
- package/dist/scripts/migrate-storage.js +17750 -0
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +9031 -0
- package/dist/scripts/migrations/v16-to-v17.js +141 -0
- package/dist/setup/detect.js +3 -0
- package/dist/setup/ripgrep-install.js +3 -0
- package/dist/setup/ripgrep-resolve.js +3 -0
- package/dist/setup/setup.js +775 -37
- package/dist/setup/steps.js +3 -15
- package/dist/sources/include.js +3 -0
- package/dist/sources/provider-factory.js +5 -12
- package/dist/sources/provider.js +3 -20
- package/dist/sources/providers/filesystem.js +19 -23
- package/dist/sources/providers/git.js +179 -20
- package/dist/sources/providers/index.js +3 -0
- package/dist/sources/providers/install-types.js +3 -13
- package/dist/sources/providers/npm.js +3 -4
- package/dist/sources/providers/provider-utils.js +3 -0
- package/dist/sources/providers/sync-from-ref.js +3 -11
- package/dist/sources/providers/tar-utils.js +3 -0
- package/dist/sources/providers/website.js +18 -22
- package/dist/sources/resolve.js +3 -0
- package/dist/sources/types.js +3 -0
- package/dist/sources/website-ingest.js +7 -0
- package/dist/tasks/backends/cron.js +203 -0
- package/dist/tasks/backends/exec-utils.js +28 -0
- package/dist/tasks/backends/index.js +24 -0
- package/dist/tasks/backends/launchd-template.xml +19 -0
- package/dist/tasks/backends/launchd.js +187 -0
- package/dist/tasks/backends/schtasks-template.xml +29 -0
- package/dist/tasks/backends/schtasks.js +215 -0
- package/dist/tasks/parser.js +211 -0
- package/dist/tasks/resolveAkmBin.js +87 -0
- package/dist/tasks/runner.js +458 -0
- package/dist/tasks/schedule.js +227 -0
- package/dist/tasks/schema.js +15 -0
- package/dist/tasks/validator.js +62 -0
- package/dist/version.js +3 -0
- package/dist/wiki/index-template.md +12 -0
- package/dist/wiki/ingest-workflow-template.md +54 -0
- package/dist/wiki/log-template.md +8 -0
- package/dist/wiki/schema-template.md +61 -0
- package/dist/wiki/wiki-templates.js +15 -0
- package/dist/wiki/wiki.js +13 -61
- package/dist/workflows/authoring.js +8 -25
- package/dist/workflows/cli.js +3 -0
- package/dist/workflows/db.js +141 -2
- package/dist/workflows/document-cache.js +3 -10
- package/dist/workflows/parser.js +3 -0
- package/dist/workflows/renderer.js +11 -3
- package/dist/workflows/runs.js +91 -89
- package/dist/workflows/schema.js +3 -0
- package/dist/workflows/scope-key.js +79 -0
- package/dist/workflows/validator.js +4 -8
- package/dist/workflows/workflow-template.md +24 -0
- package/docs/README.md +10 -2
- package/docs/data-and-telemetry.md +225 -0
- package/docs/migration/release-notes/0.7.0.md +1 -1
- package/docs/migration/release-notes/0.7.4.md +1 -1
- package/docs/migration/release-notes/0.7.5.md +20 -0
- package/docs/migration/release-notes/0.8.0.md +48 -0
- package/docs/migration/v0.7-to-v0.8.md +1307 -0
- package/package.json +29 -11
- package/dist/commands/install-audit.js +0 -381
- package/dist/commands/vault.js +0 -333
- package/dist/templates/wiki-templates.js +0 -100
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "akm-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0-rc.10",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "akm (Agent
|
|
5
|
+
"description": "akm (Agent Knowledge Management) — A package manager for AI agent skills, commands, tools, and knowledge. Works with Claude Code, OpenCode, Cursor, and any AI coding assistant.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"akm",
|
|
8
|
+
"agent-knowledge-management",
|
|
8
9
|
"agent-kit-manager",
|
|
9
10
|
"akm-cli",
|
|
10
11
|
"ai-agent",
|
|
@@ -31,26 +32,40 @@
|
|
|
31
32
|
"url": "https://github.com/itlackey/akm/issues"
|
|
32
33
|
},
|
|
33
34
|
"license": "MPL-2.0",
|
|
35
|
+
"pinNotes": {
|
|
36
|
+
"@opencode-ai/sdk@1.2.20": "Exact pin. The SDK surface we use (createOpencode + session.create/prompt/delete in src/integrations/agent/sdk-runner.ts) is stable across 1.x, but the SDK has shipped 5+ minor versions of unrelated provider/registry churn. akm-cli is a global CLI install so the pin is isolated from user-project deps. Re-test sdk-runner before bumping."
|
|
37
|
+
},
|
|
34
38
|
"files": [
|
|
35
39
|
"dist",
|
|
36
40
|
"README.md",
|
|
37
41
|
"CHANGELOG.md",
|
|
42
|
+
"SECURITY.md",
|
|
38
43
|
"LICENSE",
|
|
39
|
-
"docs/migration/release-notes"
|
|
44
|
+
"docs/migration/release-notes",
|
|
45
|
+
"docs/migration/v0.7-to-v0.8.md",
|
|
46
|
+
"docs/data-and-telemetry.md"
|
|
40
47
|
],
|
|
41
48
|
"bin": {
|
|
42
|
-
"akm": "dist/cli.js"
|
|
49
|
+
"akm": "dist/cli.js",
|
|
50
|
+
"akm-migrate-storage": "dist/scripts/migrate-storage.js"
|
|
43
51
|
},
|
|
44
52
|
"scripts": {
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"check
|
|
48
|
-
"
|
|
53
|
+
"preinstall": "node -e \"var ua=process.env.npm_config_user_agent||'';if(process.versions.bun||ua.startsWith('bun/')||process.env.BUN_INSTALL){process.exit(0)}console.error('\\n ERROR: akm-cli 0.8 requires the Bun runtime (https://bun.sh) or the prebuilt binary.\\n Running under Node.js is not supported in this release.\\n Install options:\\n 1. Bun: curl -fsSL https://bun.sh/install | bash && bun install -g akm-cli\\n 2. Binary: curl -fsSL https://github.com/itlackey/akm/releases/latest/download/install.sh | bash\\n Cross-runtime support is planned for 0.9.0.\\n');process.exit(1)\"",
|
|
54
|
+
"build": "rm -rf dist && bun run tsc --project ./tsconfig.build.json && bun scripts/copy-assets.ts",
|
|
55
|
+
"check": "bun run lint && bunx tsc --noEmit && bun run test:unit && bun run test:integration",
|
|
56
|
+
"check:changed": "bun test tests/output-baseline.test.ts tests/integration/e2e.test.ts tests/stash-search.test.ts && bun run lint && bunx tsc --noEmit",
|
|
57
|
+
"test": "bun test --parallel=12 --timeout=30000 ./tests --path-ignore-patterns=tests/integration",
|
|
58
|
+
"test:unit": "bun test --parallel=12 --timeout=30000 ./tests --path-ignore-patterns=tests/integration",
|
|
59
|
+
"test:integration": "bun test --parallel=12 --timeout=30000 ./tests/integration ./tests/commands ./tests/workflows",
|
|
60
|
+
"test:sharded": "bun test ./tests --shard=1/4 & bun test ./tests --shard=2/4 & bun test ./tests --shard=3/4 & bun test ./tests --shard=4/4 & wait",
|
|
61
|
+
"test:time": "bun scripts/test-timing-report.ts",
|
|
62
|
+
"lint:isolation": "bun scripts/lint-tests-isolation.ts",
|
|
49
63
|
"lint:devto-posts": "bun scripts/lint-devto-posts.ts",
|
|
50
64
|
"lint:devto-posts:fix": "bun scripts/lint-devto-posts.ts --fix",
|
|
51
65
|
"publish:devto": "npx -y @sinedied/devto-cli push \"docs/posts/**/*.md\" --token \"$DEVTO_TOKEN\" --repo \"$GITHUB_REPOSITORY\" --branch \"${GITHUB_REF_NAME:-main}\" --reconcile",
|
|
52
66
|
"release:check": "./tests/release-check.sh",
|
|
53
|
-
"lint": "bunx biome check src/ tests/",
|
|
67
|
+
"lint": "bunx biome check src/ tests/ && bun scripts/lint-tests-isolation.ts && bun scripts/lint-license-headers.ts",
|
|
68
|
+
"lint:tests-isolation": "bun scripts/lint-tests-isolation.ts",
|
|
54
69
|
"lint:fix": "bunx biome check --write src/ tests/",
|
|
55
70
|
"format": "bunx biome format --write src/ tests/",
|
|
56
71
|
"prepublishOnly": "cp .github/README.npm.md README.md && bun run build",
|
|
@@ -67,15 +82,18 @@
|
|
|
67
82
|
},
|
|
68
83
|
"optionalDependencies": {
|
|
69
84
|
"@huggingface/transformers": "^4.2.0",
|
|
70
|
-
"sqlite-vec": "0.1.
|
|
85
|
+
"sqlite-vec": "^0.1.9"
|
|
71
86
|
},
|
|
72
87
|
"engines": {
|
|
73
88
|
"bun": ">=1.0.0"
|
|
74
89
|
},
|
|
75
90
|
"dependencies": {
|
|
76
91
|
"@clack/prompts": "^1.3.0",
|
|
92
|
+
"@opencode-ai/sdk": "1.2.20",
|
|
77
93
|
"citty": "^0.2.2",
|
|
78
94
|
"dotenv": "^17.4.2",
|
|
79
|
-
"yaml": "^2.8.4"
|
|
95
|
+
"yaml": "^2.8.4",
|
|
96
|
+
"zod": "^3.23.0",
|
|
97
|
+
"zod-to-json-schema": "^3.23.0"
|
|
80
98
|
}
|
|
81
99
|
}
|
|
@@ -1,381 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { filterNonEmptyStrings, toPosix } from "../core/common";
|
|
4
|
-
const DEFAULT_INSTALL_AUDIT_CONFIG = {
|
|
5
|
-
enabled: true,
|
|
6
|
-
blockOnCritical: true,
|
|
7
|
-
blockUnlistedRegistries: false,
|
|
8
|
-
registryAllowlist: [],
|
|
9
|
-
allowedFindings: [],
|
|
10
|
-
};
|
|
11
|
-
const MAX_SCANNED_FILE_BYTES = 256 * 1024;
|
|
12
|
-
const LIFECYCLE_SCRIPT_NAMES = new Set([
|
|
13
|
-
"preinstall",
|
|
14
|
-
"install",
|
|
15
|
-
"postinstall",
|
|
16
|
-
"prepublish",
|
|
17
|
-
"prepublishOnly",
|
|
18
|
-
"prepare",
|
|
19
|
-
]);
|
|
20
|
-
const TEXT_FILE_EXTENSIONS = new Set([
|
|
21
|
-
".cjs",
|
|
22
|
-
".cts",
|
|
23
|
-
".js",
|
|
24
|
-
".json",
|
|
25
|
-
".jsonc",
|
|
26
|
-
".jsx",
|
|
27
|
-
".mjs",
|
|
28
|
-
".md",
|
|
29
|
-
".ps1",
|
|
30
|
-
".py",
|
|
31
|
-
".rb",
|
|
32
|
-
".sh",
|
|
33
|
-
".toml",
|
|
34
|
-
".ts",
|
|
35
|
-
".tsx",
|
|
36
|
-
".txt",
|
|
37
|
-
".yaml",
|
|
38
|
-
".yml",
|
|
39
|
-
]);
|
|
40
|
-
const BLOCKED_PACKAGE_DIRECTORIES = new Set(["node_modules", "venv", ".venv", "site-packages"]);
|
|
41
|
-
const CONTENT_RULES = [
|
|
42
|
-
{
|
|
43
|
-
id: "prompt-ignore-previous-instructions",
|
|
44
|
-
severity: "high",
|
|
45
|
-
category: "prompt-injection",
|
|
46
|
-
message: "Contains instructions to ignore prior prompts or instructions.",
|
|
47
|
-
pattern: /\b(ignore|disregard|forget)\b[^.\n]{0,100}\b(previous|prior|earlier)\b[^.\n]{0,100}\b(instructions?|prompts?|messages?)\b/i,
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
id: "prompt-reveal-hidden-secrets",
|
|
51
|
-
severity: "critical",
|
|
52
|
-
category: "prompt-injection",
|
|
53
|
-
message: "Contains instructions to reveal hidden prompts or secrets.",
|
|
54
|
-
pattern: /\b(?:reveal|print|dump|show|output|return|exfiltrat(?:e|ion))\b[^.\n]{0,60}\b(?:your|the)\b[^.\n]{0,40}\b(system prompt|hidden instructions?|developer message|api key|token|secret|password)\b/i,
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
id: "prompt-bypass-guardrails",
|
|
58
|
-
severity: "high",
|
|
59
|
-
category: "prompt-injection",
|
|
60
|
-
message: "Contains instructions to bypass safety or security controls.",
|
|
61
|
-
pattern: /\b(bypass|disable|ignore)\b[^.\n]{0,100}\b(safety|security|guardrails|restrictions|policies)\b/i,
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
id: "remote-shell-pipe",
|
|
65
|
-
severity: "critical",
|
|
66
|
-
category: "malicious-code",
|
|
67
|
-
message: "Downloads remote content and pipes it directly into a shell.",
|
|
68
|
-
pattern: /\b(curl|wget)\b[^\n|]{0,200}\|\s*(sh|bash|zsh)\b/i,
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
id: "powershell-download-exec",
|
|
72
|
-
severity: "critical",
|
|
73
|
-
category: "malicious-code",
|
|
74
|
-
message: "Downloads remote content and executes it in PowerShell.",
|
|
75
|
-
pattern: /\b(Invoke-WebRequest|iwr|curl)\b[^\n|]{0,200}\|\s*(iex|Invoke-Expression)\b/i,
|
|
76
|
-
},
|
|
77
|
-
{
|
|
78
|
-
id: "powershell-encoded-command",
|
|
79
|
-
severity: "critical",
|
|
80
|
-
category: "malicious-code",
|
|
81
|
-
message: "Uses an encoded PowerShell command.",
|
|
82
|
-
pattern: /\bpowershell(?:\.exe)?\b[^\n]{0,120}\s-(?:enc|encodedcommand)\b/i,
|
|
83
|
-
},
|
|
84
|
-
{
|
|
85
|
-
id: "credential-exfiltration-language",
|
|
86
|
-
severity: "high",
|
|
87
|
-
category: "malicious-code",
|
|
88
|
-
message: "Contains language associated with credential or secret exfiltration.",
|
|
89
|
-
pattern: /\b(exfiltrat(?:e|ion)|harvest|steal)\b[^.\n]{0,120}\b(credentials?|tokens?|secrets?|ssh keys?|passwords?|cookies?)\b/i,
|
|
90
|
-
},
|
|
91
|
-
];
|
|
92
|
-
export function resolveInstallAuditConfig(config) {
|
|
93
|
-
const installAudit = config?.security?.installAudit;
|
|
94
|
-
const allowlist = filterNonEmptyStrings(installAudit?.registryAllowlist) ??
|
|
95
|
-
filterNonEmptyStrings(installAudit?.registryWhitelist) ??
|
|
96
|
-
[];
|
|
97
|
-
return {
|
|
98
|
-
enabled: installAudit?.enabled ?? DEFAULT_INSTALL_AUDIT_CONFIG.enabled,
|
|
99
|
-
blockOnCritical: installAudit?.blockOnCritical ?? DEFAULT_INSTALL_AUDIT_CONFIG.blockOnCritical,
|
|
100
|
-
blockUnlistedRegistries: installAudit?.blockUnlistedRegistries ?? DEFAULT_INSTALL_AUDIT_CONFIG.blockUnlistedRegistries,
|
|
101
|
-
registryAllowlist: allowlist.map((entry) => entry.trim().toLowerCase()),
|
|
102
|
-
allowedFindings: installAudit?.allowedFindings ?? DEFAULT_INSTALL_AUDIT_CONFIG.allowedFindings,
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
export function enforceRegistryInstallPolicy(registryLabels, config, ref) {
|
|
106
|
-
const resolved = resolveInstallAuditConfig(config);
|
|
107
|
-
if (!resolved.blockUnlistedRegistries)
|
|
108
|
-
return;
|
|
109
|
-
if (resolved.registryAllowlist.length === 0) {
|
|
110
|
-
throw new Error(`Install blocked for ${ref}: no registries are allowlisted. Configure security.installAudit.registryAllowlist or disable security.installAudit.blockUnlistedRegistries.`);
|
|
111
|
-
}
|
|
112
|
-
const matched = registryLabels.some((label) => resolved.registryAllowlist.includes(label.toLowerCase()));
|
|
113
|
-
if (matched)
|
|
114
|
-
return;
|
|
115
|
-
throw new Error(`Install blocked for ${ref}: registry is not allowlisted. Allowed: ${resolved.registryAllowlist.join(", ")}. Seen: ${registryLabels.join(", ")}.`);
|
|
116
|
-
}
|
|
117
|
-
export function auditInstallCandidate(input) {
|
|
118
|
-
const resolved = resolveInstallAuditConfig(input.config);
|
|
119
|
-
if (!resolved.enabled) {
|
|
120
|
-
return {
|
|
121
|
-
enabled: false,
|
|
122
|
-
passed: true,
|
|
123
|
-
blocked: false,
|
|
124
|
-
trusted: false,
|
|
125
|
-
registryLabels: [...input.registryLabels],
|
|
126
|
-
findings: [],
|
|
127
|
-
scannedFiles: 0,
|
|
128
|
-
scannedBytes: 0,
|
|
129
|
-
summary: buildSummary([]),
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
const findings = [];
|
|
133
|
-
const counters = { scannedFiles: 0, scannedBytes: 0 };
|
|
134
|
-
scanDirectory(input.rootDir, input.rootDir, findings, counters);
|
|
135
|
-
const { findings: activeFindings, waivedFindings } = splitAllowedFindings(findings, input.ref, resolved.allowedFindings);
|
|
136
|
-
const summary = buildSummary(activeFindings);
|
|
137
|
-
const blocked = !input.trustThisInstall && resolved.blockOnCritical && summary.critical > 0;
|
|
138
|
-
return {
|
|
139
|
-
enabled: true,
|
|
140
|
-
passed: activeFindings.length === 0,
|
|
141
|
-
blocked,
|
|
142
|
-
trusted: Boolean(input.trustThisInstall),
|
|
143
|
-
registryLabels: [...input.registryLabels],
|
|
144
|
-
findings: activeFindings,
|
|
145
|
-
scannedFiles: counters.scannedFiles,
|
|
146
|
-
scannedBytes: counters.scannedBytes,
|
|
147
|
-
summary,
|
|
148
|
-
...(waivedFindings.length > 0 ? { waivedFindings } : {}),
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
export function formatInstallAuditFailure(ref, report) {
|
|
152
|
-
const lines = [`Security audit failed for ${ref}.`, formatInstallAuditSummary(report)];
|
|
153
|
-
for (const finding of report.findings.slice(0, 5)) {
|
|
154
|
-
lines.push(`- [${finding.severity}] ${finding.message}${finding.file ? ` (${finding.file})` : ""}`);
|
|
155
|
-
}
|
|
156
|
-
if (report.findings.length > 5) {
|
|
157
|
-
lines.push(`- ${report.findings.length - 5} more finding(s) omitted`);
|
|
158
|
-
}
|
|
159
|
-
lines.push("Disable blocking with `security.installAudit.blockOnCritical = false`, or disable audits with `security.installAudit.enabled = false`." +
|
|
160
|
-
" Or pass --trust on a one-off 'akm add' to bypass this audit for this install only.");
|
|
161
|
-
return lines.join("\n");
|
|
162
|
-
}
|
|
163
|
-
export function formatInstallAuditSummary(report) {
|
|
164
|
-
if (!report.enabled)
|
|
165
|
-
return "Audit: disabled";
|
|
166
|
-
const severitySummary = [];
|
|
167
|
-
if (report.summary.critical > 0)
|
|
168
|
-
severitySummary.push(`${report.summary.critical} critical`);
|
|
169
|
-
if (report.summary.high > 0)
|
|
170
|
-
severitySummary.push(`${report.summary.high} high`);
|
|
171
|
-
if (report.summary.moderate > 0)
|
|
172
|
-
severitySummary.push(`${report.summary.moderate} moderate`);
|
|
173
|
-
if (report.summary.low > 0)
|
|
174
|
-
severitySummary.push(`${report.summary.low} low`);
|
|
175
|
-
const detail = severitySummary.length > 0 ? severitySummary.join(", ") : "no findings";
|
|
176
|
-
const status = report.blocked ? "blocked" : report.passed ? "passed" : report.trusted ? "trusted" : "warnings";
|
|
177
|
-
const waived = report.waivedFindings?.length ? `; waived ${report.waivedFindings.length}` : "";
|
|
178
|
-
return `Audit: ${status} (${detail}; scanned ${report.scannedFiles} file${report.scannedFiles === 1 ? "" : "s"}${waived})`;
|
|
179
|
-
}
|
|
180
|
-
export function deriveRegistryLabels(input) {
|
|
181
|
-
const labels = new Set();
|
|
182
|
-
labels.add(input.source);
|
|
183
|
-
if (input.source === "github")
|
|
184
|
-
labels.add("github.com");
|
|
185
|
-
if (input.source === "npm")
|
|
186
|
-
labels.add("npm");
|
|
187
|
-
addUrlLabels(labels, input.artifactUrl);
|
|
188
|
-
addUrlLabels(labels, input.gitUrl);
|
|
189
|
-
if (input.source === "github" && input.ref.startsWith("github:")) {
|
|
190
|
-
labels.add("github");
|
|
191
|
-
}
|
|
192
|
-
return [...labels];
|
|
193
|
-
}
|
|
194
|
-
function scanDirectory(dir, rootDir, findings, counters) {
|
|
195
|
-
let entries;
|
|
196
|
-
try {
|
|
197
|
-
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
198
|
-
}
|
|
199
|
-
catch {
|
|
200
|
-
return;
|
|
201
|
-
}
|
|
202
|
-
for (const entry of entries) {
|
|
203
|
-
if (entry.name === ".git")
|
|
204
|
-
continue;
|
|
205
|
-
const fullPath = path.join(dir, entry.name);
|
|
206
|
-
if (entry.isDirectory()) {
|
|
207
|
-
if (BLOCKED_PACKAGE_DIRECTORIES.has(entry.name)) {
|
|
208
|
-
const relativePath = path.relative(rootDir, fullPath) || entry.name;
|
|
209
|
-
findings.push({
|
|
210
|
-
id: "bundled-package-directory",
|
|
211
|
-
severity: "critical",
|
|
212
|
-
category: "vendored-dependency",
|
|
213
|
-
message: `Contains bundled dependency directory "${entry.name}".`,
|
|
214
|
-
file: relativePath,
|
|
215
|
-
snippet: relativePath,
|
|
216
|
-
});
|
|
217
|
-
continue;
|
|
218
|
-
}
|
|
219
|
-
scanDirectory(fullPath, rootDir, findings, counters);
|
|
220
|
-
continue;
|
|
221
|
-
}
|
|
222
|
-
if (!entry.isFile())
|
|
223
|
-
continue;
|
|
224
|
-
scanFile(fullPath, rootDir, findings, counters);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
function scanFile(filePath, rootDir, findings, counters) {
|
|
228
|
-
const ext = path.extname(filePath).toLowerCase();
|
|
229
|
-
const basename = path.basename(filePath).toLowerCase();
|
|
230
|
-
if (basename !== "package.json" && !TEXT_FILE_EXTENSIONS.has(ext))
|
|
231
|
-
return;
|
|
232
|
-
let fileSize;
|
|
233
|
-
try {
|
|
234
|
-
fileSize = fs.statSync(filePath).size;
|
|
235
|
-
}
|
|
236
|
-
catch {
|
|
237
|
-
return;
|
|
238
|
-
}
|
|
239
|
-
const readSize = Math.min(fileSize, MAX_SCANNED_FILE_BYTES);
|
|
240
|
-
const buf = Buffer.alloc(readSize);
|
|
241
|
-
let bytesRead;
|
|
242
|
-
try {
|
|
243
|
-
const fd = fs.openSync(filePath, "r");
|
|
244
|
-
try {
|
|
245
|
-
bytesRead = fs.readSync(fd, buf, 0, readSize, 0);
|
|
246
|
-
}
|
|
247
|
-
finally {
|
|
248
|
-
fs.closeSync(fd);
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
catch {
|
|
252
|
-
return;
|
|
253
|
-
}
|
|
254
|
-
if (bytesRead === 0)
|
|
255
|
-
return;
|
|
256
|
-
const bytes = buf.subarray(0, bytesRead);
|
|
257
|
-
if (bytes.includes(0))
|
|
258
|
-
return;
|
|
259
|
-
counters.scannedFiles += 1;
|
|
260
|
-
counters.scannedBytes += bytesRead;
|
|
261
|
-
const content = bytes.toString("utf8");
|
|
262
|
-
const relativePath = path.relative(rootDir, filePath) || path.basename(filePath);
|
|
263
|
-
const genericContent = basename === "package.json" ? stripPackageJsonScripts(content) : content;
|
|
264
|
-
for (const rule of CONTENT_RULES) {
|
|
265
|
-
const match = genericContent.match(rule.pattern);
|
|
266
|
-
if (!match)
|
|
267
|
-
continue;
|
|
268
|
-
findings.push({
|
|
269
|
-
id: rule.id,
|
|
270
|
-
severity: rule.severity,
|
|
271
|
-
category: rule.category,
|
|
272
|
-
message: rule.message,
|
|
273
|
-
file: relativePath,
|
|
274
|
-
snippet: clipSnippet(match[0]),
|
|
275
|
-
});
|
|
276
|
-
}
|
|
277
|
-
if (basename === "package.json") {
|
|
278
|
-
scanPackageJson(content, relativePath, findings);
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
function stripPackageJsonScripts(content) {
|
|
282
|
-
let parsed;
|
|
283
|
-
try {
|
|
284
|
-
parsed = JSON.parse(content);
|
|
285
|
-
}
|
|
286
|
-
catch {
|
|
287
|
-
return content;
|
|
288
|
-
}
|
|
289
|
-
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
|
|
290
|
-
return content;
|
|
291
|
-
const packageJson = { ...parsed };
|
|
292
|
-
delete packageJson.scripts;
|
|
293
|
-
return JSON.stringify(packageJson, null, 2);
|
|
294
|
-
}
|
|
295
|
-
function scanPackageJson(content, relativePath, findings) {
|
|
296
|
-
let parsed;
|
|
297
|
-
try {
|
|
298
|
-
parsed = JSON.parse(content);
|
|
299
|
-
}
|
|
300
|
-
catch {
|
|
301
|
-
return;
|
|
302
|
-
}
|
|
303
|
-
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
|
|
304
|
-
return;
|
|
305
|
-
const scripts = parsed.scripts;
|
|
306
|
-
if (typeof scripts !== "object" || scripts === null || Array.isArray(scripts))
|
|
307
|
-
return;
|
|
308
|
-
for (const [name, command] of Object.entries(scripts)) {
|
|
309
|
-
if (!LIFECYCLE_SCRIPT_NAMES.has(name) || typeof command !== "string")
|
|
310
|
-
continue;
|
|
311
|
-
for (const rule of CONTENT_RULES) {
|
|
312
|
-
if (!rule.pattern.test(command))
|
|
313
|
-
continue;
|
|
314
|
-
findings.push({
|
|
315
|
-
id: `lifecycle-${name}-${rule.id}`,
|
|
316
|
-
severity: rule.severity,
|
|
317
|
-
category: "install-script",
|
|
318
|
-
message: `Lifecycle script "${name}" is suspicious: ${rule.message.toLowerCase()}`,
|
|
319
|
-
file: relativePath,
|
|
320
|
-
snippet: clipSnippet(command),
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
function clipSnippet(value) {
|
|
326
|
-
const normalized = value.replace(/\s+/g, " ").trim();
|
|
327
|
-
return normalized.length <= 140 ? normalized : `${normalized.slice(0, 137)}...`;
|
|
328
|
-
}
|
|
329
|
-
function buildSummary(findings) {
|
|
330
|
-
const summary = { low: 0, moderate: 0, high: 0, critical: 0, total: findings.length };
|
|
331
|
-
for (const finding of findings) {
|
|
332
|
-
summary[finding.severity] += 1;
|
|
333
|
-
}
|
|
334
|
-
return summary;
|
|
335
|
-
}
|
|
336
|
-
function splitAllowedFindings(findings, ref, allowedFindings) {
|
|
337
|
-
const active = [];
|
|
338
|
-
const waived = [];
|
|
339
|
-
for (const finding of findings) {
|
|
340
|
-
if (matchesAllowedFinding(finding, ref, allowedFindings)) {
|
|
341
|
-
waived.push(finding);
|
|
342
|
-
continue;
|
|
343
|
-
}
|
|
344
|
-
active.push(finding);
|
|
345
|
-
}
|
|
346
|
-
return { findings: active, waivedFindings: waived };
|
|
347
|
-
}
|
|
348
|
-
function matchesAllowedFinding(finding, ref, allowedFindings) {
|
|
349
|
-
// Normalize paths so a waiver written against `scripts/setup.sh` matches
|
|
350
|
-
// a finding emitted as `./scripts/setup.sh` or `scripts//setup.sh`. On
|
|
351
|
-
// Windows we also fold case, mirroring `isWithin`'s comparison rules.
|
|
352
|
-
const findingPathNormalized = normalizeWaiverPath(finding.file);
|
|
353
|
-
return allowedFindings.some((allowed) => {
|
|
354
|
-
if (allowed.id !== finding.id)
|
|
355
|
-
return false;
|
|
356
|
-
if (allowed.ref && allowed.ref !== ref)
|
|
357
|
-
return false;
|
|
358
|
-
if (allowed.path && normalizeWaiverPath(allowed.path) !== findingPathNormalized)
|
|
359
|
-
return false;
|
|
360
|
-
return true;
|
|
361
|
-
});
|
|
362
|
-
}
|
|
363
|
-
function normalizeWaiverPath(value) {
|
|
364
|
-
if (!value)
|
|
365
|
-
return value;
|
|
366
|
-
// Strip a leading `./` and POSIX-ify after path.normalize so Windows path
|
|
367
|
-
// separators don't trigger spurious mismatches.
|
|
368
|
-
const normalized = toPosix(path.normalize(value)).replace(/^\.\/+/, "");
|
|
369
|
-
return process.platform === "win32" ? normalized.toLowerCase() : normalized;
|
|
370
|
-
}
|
|
371
|
-
function addUrlLabels(labels, rawUrl) {
|
|
372
|
-
if (!rawUrl)
|
|
373
|
-
return;
|
|
374
|
-
try {
|
|
375
|
-
const parsed = new URL(rawUrl);
|
|
376
|
-
labels.add(parsed.hostname.toLowerCase());
|
|
377
|
-
}
|
|
378
|
-
catch {
|
|
379
|
-
// Ignore non-URL refs (for example git@host:path)
|
|
380
|
-
}
|
|
381
|
-
}
|