@apso/cli 0.15.2 → 0.15.3

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.
@@ -732,13 +732,11 @@ class McpServe extends base_command_1.default {
732
732
  ],
733
733
  };
734
734
  }
735
- // File the issue
736
- const { execSync } = await Promise.resolve().then(() => tslib_1.__importStar(require("child_process")));
737
- const url = execSync(`gh issue create --repo apsoai/cli --title "${title.replace(/"/g, '\\"')}" --body-file -`, {
738
- input: body,
739
- encoding: "utf-8",
740
- timeout: 30000,
741
- }).trim();
735
+ // File the issue. fileIssue passes the title as a discrete argv
736
+ // element and the body via a temp file (execFile, shell: false), so
737
+ // backticks / $() / quotes in user-supplied content are never
738
+ // shell-evaluated (no command injection).
739
+ const url = (0, runner_1.fileIssue)(title, body);
742
740
  return {
743
741
  content: [
744
742
  {
@@ -14,6 +14,14 @@ export declare function formatFindings(findings: DiagnosticFinding[]): string;
14
14
  * Check if the GitHub CLI is installed and authenticated.
15
15
  */
16
16
  export declare function isGhAvailable(): boolean;
17
+ /**
18
+ * Build the argv array for `gh issue list --search`.
19
+ *
20
+ * Pure function (no side effects) so it can be unit-tested. The search query
21
+ * is returned as its own array element and is NEVER concatenated into a shell
22
+ * string, so backticks / quotes / `$()` in the title are treated literally.
23
+ */
24
+ export declare function buildIssueSearchArgs(title: string): string[];
17
25
  /**
18
26
  * Search for existing issues that might duplicate what we're about to file.
19
27
  */
@@ -22,6 +30,27 @@ export declare function searchExistingIssues(title: string): {
22
30
  title: string;
23
31
  url: string;
24
32
  }[];
33
+ /**
34
+ * Build the argv array for `gh issue create`.
35
+ *
36
+ * Pure function (no side effects) so it can be unit-tested. The title is
37
+ * returned as its own array element directly after `--title` and the body is
38
+ * passed via `--body-file <path>`. Nothing is concatenated into a shell string,
39
+ * so backticks / `$()` / quotes / newlines in the title are passed literally
40
+ * (no command substitution, no injection).
41
+ */
42
+ export declare function buildIssueCreateArgs(title: string, bodyFilePath: string): string[];
43
+ /**
44
+ * File a GitHub issue with an arbitrary title and body.
45
+ *
46
+ * The body is written to a temp file and passed via `--body-file`, and the
47
+ * title is passed as a discrete argv element via execFile (shell: false). The
48
+ * child's stdin is explicitly not inherited from the parent so it cannot
49
+ * consume / blank the body.
50
+ *
51
+ * Returns the URL of the created issue.
52
+ */
53
+ export declare function fileIssue(title: string, body: string): string;
25
54
  /**
26
55
  * Build and file a GitHub issue from diagnostic findings.
27
56
  * Returns the URL of the created issue.
@@ -1,9 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.fileGitHubIssue = exports.searchExistingIssues = exports.isGhAvailable = exports.formatFindings = exports.runDiagnostics = void 0;
3
+ exports.fileGitHubIssue = exports.fileIssue = exports.buildIssueCreateArgs = exports.searchExistingIssues = exports.buildIssueSearchArgs = exports.isGhAvailable = exports.formatFindings = exports.runDiagnostics = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const child_process_1 = require("child_process");
6
+ const fs = tslib_1.__importStar(require("fs"));
6
7
  const os = tslib_1.__importStar(require("os"));
8
+ const path = tslib_1.__importStar(require("path"));
7
9
  const checks_1 = require("./checks");
8
10
  /**
9
11
  * Run all diagnostic checks and return aggregated findings.
@@ -74,7 +76,8 @@ function formatFinding(f) {
74
76
  */
75
77
  function isGhAvailable() {
76
78
  try {
77
- (0, child_process_1.execSync)("gh auth status", { stdio: "ignore" });
79
+ // execFile (shell: false) — no shell interpretation of arguments.
80
+ (0, child_process_1.execFileSync)("gh", ["auth", "status"], { stdio: "ignore" });
78
81
  return true;
79
82
  }
80
83
  catch {
@@ -82,14 +85,47 @@ function isGhAvailable() {
82
85
  }
83
86
  }
84
87
  exports.isGhAvailable = isGhAvailable;
88
+ /**
89
+ * The GitHub repository issues are filed against.
90
+ */
91
+ const ISSUE_REPO = "apsoai/cli";
92
+ /**
93
+ * Build the argv array for `gh issue list --search`.
94
+ *
95
+ * Pure function (no side effects) so it can be unit-tested. The search query
96
+ * is returned as its own array element and is NEVER concatenated into a shell
97
+ * string, so backticks / quotes / `$()` in the title are treated literally.
98
+ */
99
+ function buildIssueSearchArgs(title) {
100
+ // Extract key terms from the title for search (passed as a single argv arg).
101
+ const searchQuery = title.slice(0, 100);
102
+ return [
103
+ "issue",
104
+ "list",
105
+ "--repo",
106
+ ISSUE_REPO,
107
+ "--search",
108
+ searchQuery,
109
+ "--state",
110
+ "open",
111
+ "--json",
112
+ "number,title,url",
113
+ "--limit",
114
+ "5",
115
+ ];
116
+ }
117
+ exports.buildIssueSearchArgs = buildIssueSearchArgs;
85
118
  /**
86
119
  * Search for existing issues that might duplicate what we're about to file.
87
120
  */
88
121
  function searchExistingIssues(title) {
89
122
  try {
90
- // Extract key terms from the title for search
91
- const searchQuery = title.slice(0, 100);
92
- const result = (0, child_process_1.execSync)(`gh issue list --repo apsoai/cli --search "${searchQuery.replace(/"/g, '\\"')}" --state open --json number,title,url --limit 5`, { encoding: "utf-8", timeout: 15000 });
123
+ // execFile with an args array (shell: false). The title is passed as a
124
+ // distinct argv element, so it is never shell-evaluated.
125
+ const result = (0, child_process_1.execFileSync)("gh", buildIssueSearchArgs(title), {
126
+ encoding: "utf-8",
127
+ timeout: 15000,
128
+ });
93
129
  return JSON.parse(result);
94
130
  }
95
131
  catch {
@@ -97,6 +133,56 @@ function searchExistingIssues(title) {
97
133
  }
98
134
  }
99
135
  exports.searchExistingIssues = searchExistingIssues;
136
+ /**
137
+ * Build the argv array for `gh issue create`.
138
+ *
139
+ * Pure function (no side effects) so it can be unit-tested. The title is
140
+ * returned as its own array element directly after `--title` and the body is
141
+ * passed via `--body-file <path>`. Nothing is concatenated into a shell string,
142
+ * so backticks / `$()` / quotes / newlines in the title are passed literally
143
+ * (no command substitution, no injection).
144
+ */
145
+ function buildIssueCreateArgs(title, bodyFilePath) {
146
+ return [
147
+ "issue",
148
+ "create",
149
+ "--repo",
150
+ ISSUE_REPO,
151
+ "--title",
152
+ title,
153
+ "--body-file",
154
+ bodyFilePath,
155
+ ];
156
+ }
157
+ exports.buildIssueCreateArgs = buildIssueCreateArgs;
158
+ /**
159
+ * File a GitHub issue with an arbitrary title and body.
160
+ *
161
+ * The body is written to a temp file and passed via `--body-file`, and the
162
+ * title is passed as a discrete argv element via execFile (shell: false). The
163
+ * child's stdin is explicitly not inherited from the parent so it cannot
164
+ * consume / blank the body.
165
+ *
166
+ * Returns the URL of the created issue.
167
+ */
168
+ function fileIssue(title, body) {
169
+ const bodyFilePath = path.join(fs.mkdtempSync(path.join(os.tmpdir(), "apso-issue-")), "body.md");
170
+ try {
171
+ fs.writeFileSync(bodyFilePath, body, "utf-8");
172
+ const result = (0, child_process_1.execFileSync)("gh", buildIssueCreateArgs(title, bodyFilePath), {
173
+ encoding: "utf-8",
174
+ timeout: 30000,
175
+ // Do not pipe the parent's stdin into the child (the body is supplied via
176
+ // file). stdout/stderr are captured by execFileSync.
177
+ stdio: ["ignore", "pipe", "pipe"],
178
+ });
179
+ return result.trim();
180
+ }
181
+ finally {
182
+ fs.rmSync(path.dirname(bodyFilePath), { recursive: true, force: true });
183
+ }
184
+ }
185
+ exports.fileIssue = fileIssue;
100
186
  /**
101
187
  * Build and file a GitHub issue from diagnostic findings.
102
188
  * Returns the URL of the created issue.
@@ -105,12 +191,7 @@ function fileGitHubIssue(findings, ctx) {
105
191
  const pkg = loadCliVersion();
106
192
  const title = buildIssueTitle(findings);
107
193
  const body = buildIssueBody(findings, ctx, pkg);
108
- const result = (0, child_process_1.execSync)(`gh issue create --repo apsoai/cli --title "${title.replace(/"/g, '\\"')}" --body-file -`, {
109
- input: body,
110
- encoding: "utf-8",
111
- timeout: 30000,
112
- });
113
- return result.trim();
194
+ return fileIssue(title, body);
114
195
  }
115
196
  exports.fileGitHubIssue = fileGitHubIssue;
116
197
  function loadCliVersion() {
@@ -3,8 +3,8 @@
3
3
  @Column({
4
4
  type: 'enum',
5
5
  nullable: <%= Boolean(field.nullable) %>,
6
- enum: enums.<%= field.dataType%>,
7
- default: enums.<%= field.dataType%>.<%= field.default.toLowerCase().replace(/[^a-z0-9]/g, '_').replace(/^(\d)/, '_$1').replace(/_+/g, '_').replace(/_$/, '') %>
6
+ enum: enums.<%= field.dataType%><% if (field.default !== null) { %>,
7
+ default: enums.<%= field.dataType%>.<%= field.default.toLowerCase().replace(/[^a-z0-9]/g, '_').replace(/^(\d)/, '_$1').replace(/_+/g, '_').replace(/_$/, '') %><% } %>
8
8
  })
9
9
  <% if (field.index ) {%>
10
10
  @Index()
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@apso/cli",
3
- "version": "0.15.2",
3
+ "version": "0.15.3",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@apso/cli",
9
- "version": "0.15.2",
9
+ "version": "0.15.3",
10
10
  "license": "Apache-2.0",
11
11
  "dependencies": {
12
12
  "@electric-sql/pglite": "^0.2.17",
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.15.2",
2
+ "version": "0.15.3",
3
3
  "commands": {
4
4
  "config": {
5
5
  "id": "config",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apso/cli",
3
- "version": "0.15.2",
3
+ "version": "0.15.3",
4
4
  "mcpName": "io.github.apsoai/apso",
5
5
  "description": "Apso CLI",
6
6
  "author": "Apso by Mavric - @mavric",