@apso/cli 0.29.1 → 0.31.0

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.
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.installCoAuthorHook = exports.buildCoAuthorHookScript = exports.APSO_HOOKS_DIR = exports.APSO_COAUTHOR_TRAILER = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const child_process_1 = require("child_process");
6
+ const fs = tslib_1.__importStar(require("fs"));
7
+ const path = tslib_1.__importStar(require("path"));
8
+ /**
9
+ * The Apso co-author git trailer. Appended to commits that include
10
+ * Apso-generated work so Apso is credited as a co-author.
11
+ */
12
+ exports.APSO_COAUTHOR_TRAILER = "Co-authored-by: Apso <bot@apso.ai>";
13
+ /**
14
+ * Relative path (from the repo top-level) where the managed hook is installed.
15
+ * Modern git resolves a relative `core.hooksPath` from the repository root.
16
+ */
17
+ exports.APSO_HOOKS_DIR = ".apso/hooks";
18
+ /**
19
+ * Builds the `prepare-commit-msg` bash script that appends the Apso co-author
20
+ * trailer to commits that touch Apso-generated paths.
21
+ *
22
+ * Pure function (no side effects) so it can be unit-tested directly.
23
+ *
24
+ * Behaviour of the generated script:
25
+ * - Skips merge/squash commits (`$2` is `merge` or `squash`).
26
+ * - Honors the `APSO_NO_COAUTHOR=1` opt-out.
27
+ * - Only acts when the staged changes include an Apso-generated path
28
+ * (a path segment named `autogen/`).
29
+ * - Adds the trailer idempotently via `git interpret-trailers`, which handles
30
+ * trailer-block formatting + dedupe and coexists with other `Co-authored-by`
31
+ * trailers (e.g. Claude).
32
+ */
33
+ function buildCoAuthorHookScript() {
34
+ return `#!/usr/bin/env bash
35
+ set -euo pipefail
36
+
37
+ # Managed by Apso (@apso/cli). Appends an Apso co-author trailer to commits
38
+ # that include Apso-generated files. To opt out, set APSO_NO_COAUTHOR=1 or
39
+ # remove this hook (and unset core.hooksPath).
40
+
41
+ COMMIT_MSG_FILE="\${1:-}"
42
+ COMMIT_SOURCE="\${2:-}"
43
+
44
+ # Don't trailer merge or squash commit messages.
45
+ if [ "$COMMIT_SOURCE" = "merge" ] || [ "$COMMIT_SOURCE" = "squash" ]; then
46
+ exit 0
47
+ fi
48
+
49
+ # Opt-out.
50
+ if [ "\${APSO_NO_COAUTHOR:-}" = "1" ]; then
51
+ exit 0
52
+ fi
53
+
54
+ # Only act when the staged changes include an Apso-generated path.
55
+ if ! git diff --cached --name-only | grep -qE '(^|/)autogen/'; then
56
+ exit 0
57
+ fi
58
+
59
+ # Add the trailer idempotently. git handles formatting + dedupe and coexists
60
+ # with an existing "Co-authored-by: Claude" trailer.
61
+ git interpret-trailers --in-place --if-exists addIfDifferent \\
62
+ --trailer "${exports.APSO_COAUTHOR_TRAILER}" "$COMMIT_MSG_FILE"
63
+ `;
64
+ }
65
+ exports.buildCoAuthorHookScript = buildCoAuthorHookScript;
66
+ /**
67
+ * Installs the Apso co-author `prepare-commit-msg` hook into a project.
68
+ *
69
+ * Best-effort: never throws. Callers can log the result.
70
+ *
71
+ * - Respects the `disabled` option and the `APSO_NO_COAUTHOR=1` env opt-out.
72
+ * - No-ops (returns `not-a-git-repo`) outside a git work tree.
73
+ * - Writes `<projectRoot>/.apso/hooks/prepare-commit-msg` (mode 0755).
74
+ * - Sets the repo-local `core.hooksPath` to `.apso/hooks` when unset; leaves it
75
+ * alone (and reports success) when it's already `.apso/hooks`; refuses to
76
+ * clobber a custom `core.hooksPath` (husky etc.) and reports
77
+ * `custom-hookspath` so the caller can guide the user.
78
+ */
79
+ function installCoAuthorHook(projectRoot, opts = {}) {
80
+ try {
81
+ if (opts.disabled || process.env.APSO_NO_COAUTHOR === "1") {
82
+ return { installed: false, reason: "disabled" };
83
+ }
84
+ // Verify we're inside a git work tree.
85
+ try {
86
+ (0, child_process_1.execFileSync)("git", ["-C", projectRoot, "rev-parse", "--is-inside-work-tree"], {
87
+ stdio: "ignore",
88
+ });
89
+ }
90
+ catch {
91
+ return { installed: false, reason: "not-a-git-repo" };
92
+ }
93
+ // Write the hook script.
94
+ const hooksDir = path.join(projectRoot, ".apso", "hooks");
95
+ fs.mkdirSync(hooksDir, { recursive: true });
96
+ const hookPath = path.join(hooksDir, "prepare-commit-msg");
97
+ fs.writeFileSync(hookPath, buildCoAuthorHookScript(), { mode: 0o755 });
98
+ // writeFileSync's mode is subject to umask on create and ignored on
99
+ // overwrite; chmod explicitly so the hook is always executable.
100
+ fs.chmodSync(hookPath, 0o755);
101
+ // Inspect the current core.hooksPath.
102
+ let currentHooksPath = "";
103
+ try {
104
+ currentHooksPath = (0, child_process_1.execFileSync)("git", ["-C", projectRoot, "config", "--get", "core.hooksPath"], { encoding: "utf8" }).trim();
105
+ }
106
+ catch {
107
+ // `git config --get` exits non-zero when the key is unset.
108
+ currentHooksPath = "";
109
+ }
110
+ if (currentHooksPath === "") {
111
+ (0, child_process_1.execFileSync)("git", ["-C", projectRoot, "config", "core.hooksPath", exports.APSO_HOOKS_DIR], { stdio: "ignore" });
112
+ return { installed: true };
113
+ }
114
+ if (currentHooksPath === exports.APSO_HOOKS_DIR) {
115
+ return { installed: true };
116
+ }
117
+ // Some other hooks path is configured (husky, etc.). Don't clobber it.
118
+ return { installed: false, reason: "custom-hookspath" };
119
+ }
120
+ catch {
121
+ return { installed: false, reason: "error" };
122
+ }
123
+ }
124
+ exports.installCoAuthorHook = installCoAuthorHook;
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@apso/cli",
3
- "version": "0.29.1",
3
+ "version": "0.31.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@apso/cli",
9
- "version": "0.29.1",
9
+ "version": "0.31.0",
10
10
  "license": "Apache-2.0",
11
11
  "dependencies": {
12
12
  "@biomejs/biome": "^1.9.4",
@@ -22,6 +22,7 @@
22
22
  "eta": "^2.0.0",
23
23
  "inquirer": "^8.2.7",
24
24
  "ora": "^5.4.1",
25
+ "pg": "^8.21.0",
25
26
  "pluralize": "^8.0.0",
26
27
  "posthog-node": "^5.21.2",
27
28
  "rc": "^1.2.8",
@@ -41,6 +42,7 @@
41
42
  "@types/inquirer": "^8.2.12",
42
43
  "@types/mocha": "^10",
43
44
  "@types/node": "^20.19.43",
45
+ "@types/pg": "^8.20.0",
44
46
  "@types/pluralize": "^0.0.29",
45
47
  "@types/rc": "^1.2.1",
46
48
  "@types/shelljs": "^0.8.15",
@@ -2877,6 +2879,18 @@
2877
2879
  "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==",
2878
2880
  "dev": true
2879
2881
  },
2882
+ "node_modules/@types/pg": {
2883
+ "version": "8.20.0",
2884
+ "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.20.0.tgz",
2885
+ "integrity": "sha512-bEPFOaMAHTEP1EzpvHTbmwR8UsFyHSKsRisLIHVMXnpNefSbGA1bD6CVy+qKjGSqmZqNqBDV2azOBo8TgkcVow==",
2886
+ "dev": true,
2887
+ "license": "MIT",
2888
+ "dependencies": {
2889
+ "@types/node": "*",
2890
+ "pg-protocol": "*",
2891
+ "pg-types": "^2.2.0"
2892
+ }
2893
+ },
2880
2894
  "node_modules/@types/pluralize": {
2881
2895
  "version": "0.0.29",
2882
2896
  "resolved": "https://registry.npmjs.org/@types/pluralize/-/pluralize-0.0.29.tgz",
@@ -13612,6 +13626,95 @@
13612
13626
  "node": "*"
13613
13627
  }
13614
13628
  },
13629
+ "node_modules/pg": {
13630
+ "version": "8.22.0",
13631
+ "resolved": "https://registry.npmjs.org/pg/-/pg-8.22.0.tgz",
13632
+ "integrity": "sha512-8wih1vVIBMxoUM2oB4soJsD9tDnDpLv4OXBJ+EJzFsvycD+lfyIreC2gGHq78f8jbLLt+bvlPTFdFZfJkOuzAA==",
13633
+ "license": "MIT",
13634
+ "dependencies": {
13635
+ "pg-connection-string": "^2.14.0",
13636
+ "pg-pool": "^3.14.0",
13637
+ "pg-protocol": "^1.15.0",
13638
+ "pg-types": "2.2.0",
13639
+ "pgpass": "1.0.5"
13640
+ },
13641
+ "engines": {
13642
+ "node": ">= 16.0.0"
13643
+ },
13644
+ "optionalDependencies": {
13645
+ "pg-cloudflare": "^1.4.0"
13646
+ },
13647
+ "peerDependencies": {
13648
+ "pg-native": ">=3.0.1"
13649
+ },
13650
+ "peerDependenciesMeta": {
13651
+ "pg-native": {
13652
+ "optional": true
13653
+ }
13654
+ }
13655
+ },
13656
+ "node_modules/pg-cloudflare": {
13657
+ "version": "1.4.0",
13658
+ "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.4.0.tgz",
13659
+ "integrity": "sha512-Vo7z/6rrQYxpNRylp4Tlob2elzbh+N/MOQbxFVWCxS7oEx6jF53GTJFxK2WWpKuBRkmiin4Mt+xofFDjx09R0A==",
13660
+ "license": "MIT",
13661
+ "optional": true
13662
+ },
13663
+ "node_modules/pg-connection-string": {
13664
+ "version": "2.14.0",
13665
+ "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.14.0.tgz",
13666
+ "integrity": "sha512-XwWDGcLRGCXAR8F/AM5bG7Q+A3Wm2s6QeEjlOKZLlH3UYcguiqCWKyWXVag5TLTIjR7oOJUY8kcADaZgWPyLeg==",
13667
+ "license": "MIT"
13668
+ },
13669
+ "node_modules/pg-int8": {
13670
+ "version": "1.0.1",
13671
+ "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
13672
+ "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
13673
+ "license": "ISC",
13674
+ "engines": {
13675
+ "node": ">=4.0.0"
13676
+ }
13677
+ },
13678
+ "node_modules/pg-pool": {
13679
+ "version": "3.14.0",
13680
+ "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.14.0.tgz",
13681
+ "integrity": "sha512-gKtPkFdQPU3DksooVLi9LsjZxrsBUZIpa+7aVx+LV5pNh0KzP4Zleud2po+ConrxbuXGBJ6Hfer6hdgpIBpBaw==",
13682
+ "license": "MIT",
13683
+ "peerDependencies": {
13684
+ "pg": ">=8.0"
13685
+ }
13686
+ },
13687
+ "node_modules/pg-protocol": {
13688
+ "version": "1.15.0",
13689
+ "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.15.0.tgz",
13690
+ "integrity": "sha512-cq9sECI5s0+uPUXjbz8ioyPJni6RzsRib0US67i5IoTZKw8fNeYlVE7u8F4dG7vEJJtc5wdD1K189lCCUwqWTQ==",
13691
+ "license": "MIT"
13692
+ },
13693
+ "node_modules/pg-types": {
13694
+ "version": "2.2.0",
13695
+ "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
13696
+ "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
13697
+ "license": "MIT",
13698
+ "dependencies": {
13699
+ "pg-int8": "1.0.1",
13700
+ "postgres-array": "~2.0.0",
13701
+ "postgres-bytea": "~1.0.0",
13702
+ "postgres-date": "~1.0.4",
13703
+ "postgres-interval": "^1.1.0"
13704
+ },
13705
+ "engines": {
13706
+ "node": ">=4"
13707
+ }
13708
+ },
13709
+ "node_modules/pgpass": {
13710
+ "version": "1.0.5",
13711
+ "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
13712
+ "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
13713
+ "license": "MIT",
13714
+ "dependencies": {
13715
+ "split2": "^4.1.0"
13716
+ }
13717
+ },
13615
13718
  "node_modules/picocolors": {
13616
13719
  "version": "1.0.0",
13617
13720
  "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
@@ -13736,6 +13839,45 @@
13736
13839
  "node": ">= 0.4"
13737
13840
  }
13738
13841
  },
13842
+ "node_modules/postgres-array": {
13843
+ "version": "2.0.0",
13844
+ "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
13845
+ "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
13846
+ "license": "MIT",
13847
+ "engines": {
13848
+ "node": ">=4"
13849
+ }
13850
+ },
13851
+ "node_modules/postgres-bytea": {
13852
+ "version": "1.0.1",
13853
+ "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz",
13854
+ "integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==",
13855
+ "license": "MIT",
13856
+ "engines": {
13857
+ "node": ">=0.10.0"
13858
+ }
13859
+ },
13860
+ "node_modules/postgres-date": {
13861
+ "version": "1.0.7",
13862
+ "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
13863
+ "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
13864
+ "license": "MIT",
13865
+ "engines": {
13866
+ "node": ">=0.10.0"
13867
+ }
13868
+ },
13869
+ "node_modules/postgres-interval": {
13870
+ "version": "1.2.0",
13871
+ "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
13872
+ "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
13873
+ "license": "MIT",
13874
+ "dependencies": {
13875
+ "xtend": "^4.0.0"
13876
+ },
13877
+ "engines": {
13878
+ "node": ">=0.10.0"
13879
+ }
13880
+ },
13739
13881
  "node_modules/posthog-node": {
13740
13882
  "version": "5.21.2",
13741
13883
  "resolved": "https://registry.npmjs.org/posthog-node/-/posthog-node-5.21.2.tgz",
@@ -15482,6 +15624,15 @@
15482
15624
  "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==",
15483
15625
  "dev": true
15484
15626
  },
15627
+ "node_modules/split2": {
15628
+ "version": "4.2.0",
15629
+ "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
15630
+ "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
15631
+ "license": "ISC",
15632
+ "engines": {
15633
+ "node": ">= 10.x"
15634
+ }
15635
+ },
15485
15636
  "node_modules/sprintf-js": {
15486
15637
  "version": "1.0.3",
15487
15638
  "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
@@ -17275,7 +17426,6 @@
17275
17426
  "version": "4.0.2",
17276
17427
  "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
17277
17428
  "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
17278
- "dev": true,
17279
17429
  "engines": {
17280
17430
  "node": ">=0.4"
17281
17431
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.29.1",
2
+ "version": "0.31.0",
3
3
  "commands": {
4
4
  "config": {
5
5
  "id": "config",
@@ -627,6 +627,58 @@
627
627
  },
628
628
  "args": {}
629
629
  },
630
+ "import:supabase": {
631
+ "id": "import:supabase",
632
+ "description": "Introspect an existing Supabase (Postgres) database and generate a local .apsorc schema. Read-only: it never writes to the source database.",
633
+ "strict": true,
634
+ "pluginName": "@apso/cli",
635
+ "pluginAlias": "@apso/cli",
636
+ "pluginType": "core",
637
+ "aliases": [],
638
+ "hiddenAliases": [],
639
+ "examples": [
640
+ "$ apso import supabase",
641
+ "$ apso import supabase -c \"postgresql://postgres:[pw]@db.[ref].supabase.co:5432/postgres\"",
642
+ "$ SUPABASE_DB_URL=\"postgresql://...\" apso import supabase --dry-run"
643
+ ],
644
+ "flags": {
645
+ "connection-string": {
646
+ "name": "connection-string",
647
+ "type": "option",
648
+ "char": "c",
649
+ "description": "Postgres connection string. Falls back to SUPABASE_DB_URL or DATABASE_URL, then prompts.",
650
+ "multiple": false
651
+ },
652
+ "schema": {
653
+ "name": "schema",
654
+ "type": "option",
655
+ "description": "Database schema to import",
656
+ "multiple": false,
657
+ "default": "public"
658
+ },
659
+ "out": {
660
+ "name": "out",
661
+ "type": "option",
662
+ "char": "o",
663
+ "description": "Path to write the .apsorc (default: existing .apsorc or ./.apsorc)",
664
+ "multiple": false
665
+ },
666
+ "dry-run": {
667
+ "name": "dry-run",
668
+ "type": "boolean",
669
+ "description": "Introspect and print a summary without writing any file",
670
+ "allowNo": false
671
+ },
672
+ "yes": {
673
+ "name": "yes",
674
+ "type": "boolean",
675
+ "char": "y",
676
+ "description": "Skip the overwrite confirmation prompt",
677
+ "allowNo": false
678
+ }
679
+ },
680
+ "args": {}
681
+ },
630
682
  "mcp:serve": {
631
683
  "id": "mcp:serve",
632
684
  "description": "Start an MCP server exposing Apso tools over stdio. Used by AI coding agents (Claude Code, Cursor, etc.) to design schemas, generate APIs, and deploy backends.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apso/cli",
3
- "version": "0.29.1",
3
+ "version": "0.31.0",
4
4
  "mcpName": "io.github.apsoai/apso",
5
5
  "description": "Apso CLI",
6
6
  "author": "Apso by Mavric - @mavric",
@@ -37,6 +37,7 @@
37
37
  "eta": "^2.0.0",
38
38
  "inquirer": "^8.2.7",
39
39
  "ora": "^5.4.1",
40
+ "pg": "^8.21.0",
40
41
  "pluralize": "^8.0.0",
41
42
  "posthog-node": "^5.21.2",
42
43
  "rc": "^1.2.8",
@@ -53,6 +54,7 @@
53
54
  "@types/inquirer": "^8.2.12",
54
55
  "@types/mocha": "^10",
55
56
  "@types/node": "^20.19.43",
57
+ "@types/pg": "^8.20.0",
56
58
  "@types/pluralize": "^0.0.29",
57
59
  "@types/rc": "^1.2.1",
58
60
  "@types/shelljs": "^0.8.15",
@@ -98,6 +100,9 @@
98
100
  },
99
101
  "mcp": {
100
102
  "description": "MCP server for AI coding agents"
103
+ },
104
+ "import": {
105
+ "description": "Import an existing database into a local .apsorc schema"
101
106
  }
102
107
  },
103
108
  "update": {