@jskit-ai/jskit-cli 0.2.80 → 0.2.82

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.
Files changed (32) hide show
  1. package/package.json +6 -4
  2. package/src/server/appBlueprint.js +1 -1
  3. package/src/server/commandHandlers/helperMap.js +104 -0
  4. package/src/server/commandHandlers/session.js +179 -4
  5. package/src/server/commandHandlers/show.js +169 -34
  6. package/src/server/core/argParser.js +20 -0
  7. package/src/server/core/commandCatalog.js +70 -7
  8. package/src/server/core/createCommandHandlers.js +4 -1
  9. package/src/server/helperMap.js +463 -0
  10. package/src/server/helperMapPaths.js +7 -0
  11. package/src/server/sessionRuntime/appReadiness.js +55 -0
  12. package/src/server/sessionRuntime/constants.js +298 -135
  13. package/src/server/sessionRuntime/paths.js +2 -4
  14. package/src/server/sessionRuntime/preconditions.js +393 -26
  15. package/src/server/sessionRuntime/promptRenderer.js +15 -2
  16. package/src/server/sessionRuntime/prompts/app_blueprint.md +26 -2
  17. package/src/server/sessionRuntime/prompts/automated_checks.md +42 -0
  18. package/src/server/sessionRuntime/prompts/deep_ui_check.md +53 -0
  19. package/src/server/sessionRuntime/prompts/doctor_failure.md +21 -1
  20. package/src/server/sessionRuntime/prompts/execute_plan.md +61 -0
  21. package/src/server/sessionRuntime/prompts/final_comment.md +3 -1
  22. package/src/server/sessionRuntime/prompts/issue_details.md +52 -0
  23. package/src/server/sessionRuntime/prompts/new_issue.md +34 -3
  24. package/src/server/sessionRuntime/prompts/plan_issue.md +81 -0
  25. package/src/server/sessionRuntime/prompts/pr_failure.md +14 -1
  26. package/src/server/sessionRuntime/prompts/review_changes.md +77 -15
  27. package/src/server/sessionRuntime/prompts/update_blueprint.md +36 -0
  28. package/src/server/sessionRuntime/prompts/user_check.md +22 -4
  29. package/src/server/sessionRuntime/responses.js +877 -30
  30. package/src/server/sessionRuntime/worktrees.js +31 -0
  31. package/src/server/sessionRuntime.js +2070 -244
  32. package/src/server/sessionRuntime/prompts/implement_issue.md +0 -25
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jskit-ai/jskit-cli",
3
- "version": "0.2.80",
3
+ "version": "0.2.82",
4
4
  "description": "Bundle and package orchestration CLI for JSKIT apps.",
5
5
  "type": "module",
6
6
  "files": [
@@ -20,9 +20,11 @@
20
20
  "test": "node --test"
21
21
  },
22
22
  "dependencies": {
23
- "@jskit-ai/jskit-catalog": "0.1.79",
24
- "@jskit-ai/kernel": "0.1.71",
25
- "@jskit-ai/shell-web": "0.1.70"
23
+ "@jskit-ai/jskit-catalog": "0.1.81",
24
+ "@jskit-ai/kernel": "0.1.73",
25
+ "@jskit-ai/shell-web": "0.1.72",
26
+ "@vue/compiler-sfc": "^3.5.29",
27
+ "ts-morph": "^28.0.0"
26
28
  },
27
29
  "engines": {
28
30
  "node": ">=20 <23"
@@ -28,7 +28,7 @@ function resolveAppBlueprintPaths(targetRoot = process.cwd()) {
28
28
  function extractAppBlueprintText(value = "") {
29
29
  const text = normalizeText(value);
30
30
  const match = /\[app_blueprint\]([\s\S]*?)\[\/app_blueprint\]/u.exec(text);
31
- return normalizeText(match ? match[1] : text);
31
+ return normalizeText(match ? match[1] : "");
32
32
  }
33
33
 
34
34
  async function readAppPromptTemplate(paths, templateName) {
@@ -0,0 +1,104 @@
1
+ function writeJson(stdout, payload) {
2
+ stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
3
+ }
4
+
5
+ function writeErrors(stdout, payload) {
6
+ for (const error of payload.errors || []) {
7
+ stdout.write(`[${error.code}] ${error.message}\n`);
8
+ if (error.repairCommand) {
9
+ stdout.write(`Repair: ${error.repairCommand}\n`);
10
+ }
11
+ }
12
+ }
13
+
14
+ function writeHelperMapText(stdout, payload) {
15
+ if (Object.hasOwn(payload, "changed")) {
16
+ stdout.write(
17
+ payload.changed
18
+ ? `Updated helper map at ${payload.helperMapMarkdownPath}.\n`
19
+ : `Helper map is up to date at ${payload.helperMapMarkdownPath}.\n`
20
+ );
21
+ return;
22
+ }
23
+ if (payload.exists === false) {
24
+ stdout.write(`No helper map set at ${payload.helperMapMarkdownPath}. Run jskit helper-map update.\n`);
25
+ return;
26
+ }
27
+ if (payload.markdown) {
28
+ stdout.write(payload.markdown);
29
+ return;
30
+ }
31
+ stdout.write(`Helper map is up to date at ${payload.helperMapMarkdownPath}.\n`);
32
+ }
33
+
34
+ function createHelperMapCommands(ctx = {}) {
35
+ const { resolveAppRootFromCwd } = ctx;
36
+
37
+ async function commandHelperMap({
38
+ positional = [],
39
+ options = {},
40
+ cwd,
41
+ stdout
42
+ } = {}) {
43
+ const subcommand = String(positional[0] || "").trim();
44
+ let payload;
45
+
46
+ try {
47
+ const appRoot = await resolveAppRootFromCwd(cwd);
48
+ if (positional.length > 1) {
49
+ payload = {
50
+ ok: false,
51
+ errors: [
52
+ {
53
+ code: "unexpected_helper_map_argument",
54
+ message: `Unexpected helper-map argument: ${positional.slice(1).join(" ")}`,
55
+ repairCommand: "jskit helper-map"
56
+ }
57
+ ]
58
+ };
59
+ } else if (!subcommand) {
60
+ const { readHelperMap } = await import("../helperMap.js");
61
+ payload = await readHelperMap({ targetRoot: appRoot });
62
+ } else if (subcommand === "update") {
63
+ const { updateHelperMap } = await import("../helperMap.js");
64
+ payload = await updateHelperMap({ targetRoot: appRoot });
65
+ } else {
66
+ payload = {
67
+ ok: false,
68
+ errors: [
69
+ {
70
+ code: "unknown_helper_map_subcommand",
71
+ message: `Unknown helper-map subcommand: ${subcommand}`,
72
+ repairCommand: "jskit helper-map update"
73
+ }
74
+ ]
75
+ };
76
+ }
77
+ } catch (error) {
78
+ payload = {
79
+ ok: false,
80
+ errors: [
81
+ {
82
+ code: "helper_map_failed",
83
+ message: String(error?.message || error),
84
+ repairCommand: "jskit helper-map update"
85
+ }
86
+ ]
87
+ };
88
+ }
89
+
90
+ if (options.json) {
91
+ writeJson(stdout, payload);
92
+ } else if (payload.ok === false) {
93
+ writeErrors(stdout, payload);
94
+ } else {
95
+ writeHelperMapText(stdout, payload);
96
+ }
97
+
98
+ return payload.ok === false ? 1 : 0;
99
+ }
100
+
101
+ return { commandHelperMap };
102
+ }
103
+
104
+ export { createHelperMapCommands };
@@ -4,6 +4,7 @@ import {
4
4
  adoptCodexThreadId,
5
5
  buildSessionErrorResponse,
6
6
  createSession,
7
+ inspectSessionDiff,
7
8
  inspectSessionDetails,
8
9
  listSessions,
9
10
  runSessionStep
@@ -52,6 +53,17 @@ function writeSessionText(stdout, payload) {
52
53
  stdout.write(payload.prompt);
53
54
  stdout.write("\n");
54
55
  }
56
+ if (payload.gitStatus !== undefined && payload.unstagedDiff !== undefined) {
57
+ stdout.write("\nGit status:\n");
58
+ stdout.write(payload.gitStatus || "No changes.");
59
+ stdout.write("\n");
60
+ const diff = [payload.stagedDiff, payload.unstagedDiff, payload.untrackedDiff].filter(Boolean).join("\n");
61
+ if (diff) {
62
+ stdout.write("\nDiff:\n");
63
+ stdout.write(diff);
64
+ stdout.write("\n");
65
+ }
66
+ }
55
67
  if (payload.errors?.length) {
56
68
  stdout.write("Errors:\n");
57
69
  for (const error of payload.errors) {
@@ -135,6 +147,34 @@ async function resolveStepInputs({
135
147
  cwd,
136
148
  sessionId
137
149
  }) {
150
+ if (Object.hasOwn(inlineOptions, "blueprint") || Object.hasOwn(inlineOptions, "blueprint-file")) {
151
+ return {
152
+ ok: false,
153
+ payload: buildSessionErrorResponse({
154
+ targetRoot: cwd,
155
+ sessionId,
156
+ code: "session_blueprint_input_removed",
157
+ message: "The session blueprint step no longer accepts --blueprint input. Run the step to get the Codex prompt, let Codex edit .jskit/APP_BLUEPRINT.md, then run the step again.",
158
+ repairCommand: `jskit session ${sessionId} step`
159
+ })
160
+ };
161
+ }
162
+
163
+ const issueTitle = await resolveTextInput({
164
+ codePrefix: "issue_title",
165
+ fileOption: "issue-title-file",
166
+ inlineOptions,
167
+ io,
168
+ repairCommand: `jskit session ${sessionId} step --issue-title "<title>" --issue -`,
169
+ cwd,
170
+ sessionId,
171
+ stdinOption: "-",
172
+ textOption: "issue-title"
173
+ });
174
+ if (issueTitle.ok === false) {
175
+ return issueTitle;
176
+ }
177
+
138
178
  const issue = await resolveTextInput({
139
179
  codePrefix: "issue",
140
180
  fileOption: "issue-file",
@@ -150,18 +190,138 @@ async function resolveStepInputs({
150
190
  return issue;
151
191
  }
152
192
 
193
+ const plan = await resolveTextInput({
194
+ codePrefix: "plan",
195
+ fileOption: "plan-file",
196
+ inlineOptions,
197
+ io,
198
+ repairCommand: `jskit session ${sessionId} step --plan -`,
199
+ cwd,
200
+ sessionId,
201
+ stdinOption: "-",
202
+ textOption: "plan"
203
+ });
204
+ if (plan.ok === false) {
205
+ return plan;
206
+ }
207
+
208
+ const issueDetails = await resolveTextInput({
209
+ codePrefix: "issue_details",
210
+ fileOption: "issue-details-file",
211
+ inlineOptions,
212
+ io,
213
+ repairCommand: `jskit session ${sessionId} step --issue-details -`,
214
+ cwd,
215
+ sessionId,
216
+ stdinOption: "-",
217
+ textOption: "issue-details"
218
+ });
219
+ if (issueDetails.ok === false) {
220
+ return issueDetails;
221
+ }
222
+
223
+ const reworkNotes = await resolveTextInput({
224
+ codePrefix: "rework_notes",
225
+ fileOption: "rework-notes-file",
226
+ inlineOptions,
227
+ io,
228
+ repairCommand: `jskit session ${sessionId} step --user-check failed --rework-notes -`,
229
+ cwd,
230
+ sessionId,
231
+ stdinOption: "-",
232
+ textOption: "rework-notes"
233
+ });
234
+ if (reworkNotes.ok === false) {
235
+ return reworkNotes;
236
+ }
237
+
238
+ const skipReason = await resolveTextInput({
239
+ codePrefix: "skip_reason",
240
+ fileOption: "skip-reason-file",
241
+ inlineOptions,
242
+ io,
243
+ repairCommand: `jskit session ${sessionId} step --skip-ui-check --skip-reason "<reason>"`,
244
+ cwd,
245
+ sessionId,
246
+ stdinOption: "-",
247
+ textOption: "skip-reason"
248
+ });
249
+ if (skipReason.ok === false) {
250
+ return skipReason;
251
+ }
252
+
253
+ const agentDecisions = await resolveTextInput({
254
+ codePrefix: "agent_decisions",
255
+ fileOption: "agent-decisions-file",
256
+ inlineOptions,
257
+ io,
258
+ repairCommand: `jskit session ${sessionId} step --agent-decisions -`,
259
+ cwd,
260
+ sessionId,
261
+ stdinOption: "-",
262
+ textOption: "agent-decisions"
263
+ });
264
+ if (agentDecisions.ok === false) {
265
+ return agentDecisions;
266
+ }
267
+
268
+ const closeReason = await resolveTextInput({
269
+ codePrefix: "close_reason",
270
+ fileOption: "close-reason-file",
271
+ inlineOptions,
272
+ io,
273
+ repairCommand: `jskit session ${sessionId} step --close-without-merge --close-reason "<reason>"`,
274
+ cwd,
275
+ sessionId,
276
+ stdinOption: "-",
277
+ textOption: "close-reason"
278
+ });
279
+ if (closeReason.ok === false) {
280
+ return closeReason;
281
+ }
282
+
153
283
  return {
284
+ agentDecisions: agentDecisions.value,
285
+ closeReason: closeReason.value,
154
286
  issue: issue.value,
155
- ok: true
287
+ issueTitle: issueTitle.value,
288
+ ok: true,
289
+ plan: plan.value,
290
+ issueDetails: issueDetails.value,
291
+ reworkNotes: reworkNotes.value,
292
+ skipReason: skipReason.value
156
293
  };
157
294
  }
158
295
 
159
296
  function normalizeStepOptions(inlineOptions = {}) {
160
- return {
297
+ const options = {
161
298
  ...inlineOptions,
299
+ closeWithoutMerge: inlineOptions["close-without-merge"] === "true" || inlineOptions.closeWithoutMerge === true,
300
+ mergePr: inlineOptions["merge-pr"] === "true" || inlineOptions.mergePr === true,
162
301
  prompt: inlineOptions.prompt,
302
+ reviewFindings: inlineOptions["review-findings"] || inlineOptions.reviewFindings,
303
+ skipUiCheck: inlineOptions["skip-ui-check"] === "true" || inlineOptions.skipUiCheck === true,
163
304
  userCheck: inlineOptions["user-check"] || inlineOptions.userCheck
164
305
  };
306
+ if (Object.hasOwn(inlineOptions, "review-findings-remaining") || Object.hasOwn(inlineOptions, "reviewFindingsRemaining")) {
307
+ options.reviewFindingsRemaining = inlineOptions["review-findings-remaining"] === "true" ||
308
+ inlineOptions.reviewFindingsRemaining === true;
309
+ }
310
+ return options;
311
+ }
312
+
313
+ function resolveListArchiveOption(options = {}) {
314
+ const archives = [];
315
+ if (options.abandoned) {
316
+ archives.push("abandoned");
317
+ }
318
+ if (options.completed) {
319
+ archives.push("completed");
320
+ }
321
+ if (options.all) {
322
+ archives.push("all");
323
+ }
324
+ return archives.length > 0 ? archives : "active";
165
325
  }
166
326
 
167
327
  function createSessionCommands() {
@@ -178,7 +338,10 @@ function createSessionCommands() {
178
338
  let payload;
179
339
 
180
340
  if (!first) {
181
- payload = await listSessions({ targetRoot: cwd });
341
+ payload = await listSessions({
342
+ targetRoot: cwd,
343
+ archive: resolveListArchiveOption(options)
344
+ });
182
345
  } else if (first === "create") {
183
346
  payload = await createSession({ targetRoot: cwd });
184
347
  } else if (second === "step") {
@@ -195,7 +358,14 @@ function createSessionCommands() {
195
358
  sessionId: first,
196
359
  options: {
197
360
  ...normalizeStepOptions(inlineOptions),
198
- issue: stepInputs.issue
361
+ issue: stepInputs.issue,
362
+ issueTitle: stepInputs.issueTitle,
363
+ plan: stepInputs.plan,
364
+ issueDetails: stepInputs.issueDetails,
365
+ reworkNotes: stepInputs.reworkNotes,
366
+ skipReason: stepInputs.skipReason,
367
+ agentDecisions: stepInputs.agentDecisions,
368
+ closeReason: stepInputs.closeReason
199
369
  }
200
370
  });
201
371
  } else if (second === "abandon") {
@@ -203,6 +373,11 @@ function createSessionCommands() {
203
373
  targetRoot: cwd,
204
374
  sessionId: first
205
375
  });
376
+ } else if (second === "diff") {
377
+ payload = await inspectSessionDiff({
378
+ targetRoot: cwd,
379
+ sessionId: first
380
+ });
206
381
  } else if (second === "adopt-codex-thread") {
207
382
  payload = await adoptCodexThreadId({
208
383
  targetRoot: cwd,
@@ -12,10 +12,17 @@ function createShowCommand(ctx = {}) {
12
12
  createColorFormatter,
13
13
  resolveWrapWidth,
14
14
  writeWrappedItems,
15
+ normalizeRelativePath,
15
16
  normalizeRelativePosixPath,
17
+ resolveAppRootFromCwd,
16
18
  resolvePackageIdInput,
17
19
  loadPackageRegistry,
18
20
  loadBundleRegistry,
21
+ loadAppLocalPackageRegistry,
22
+ loadLockFile,
23
+ mergePackageRegistries,
24
+ path,
25
+ fileExists,
19
26
  inspectPackageOfferings,
20
27
  buildFileWriteGroups,
21
28
  listDeclaredCapabilities,
@@ -28,7 +35,149 @@ function createShowCommand(ctx = {}) {
28
35
  deriveProviderDisplayName
29
36
  } = ctx;
30
37
 
31
- async function commandShow({ positional, options, stdout }) {
38
+ async function renderPackageEntry({
39
+ packageRegistry,
40
+ packageEntry,
41
+ options,
42
+ stdout,
43
+ color
44
+ }) {
45
+ const {
46
+ payload,
47
+ provides,
48
+ requires,
49
+ capabilityDetails
50
+ } = await buildPackageShowPayload({
51
+ packageRegistry,
52
+ packageEntry,
53
+ options,
54
+ inspectPackageOfferings,
55
+ buildFileWriteGroups,
56
+ listDeclaredCapabilities,
57
+ buildCapabilityDetailsForPackage
58
+ });
59
+
60
+ if (options.json) {
61
+ stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
62
+ } else {
63
+ renderPackagePayloadText({
64
+ payload,
65
+ provides,
66
+ requires,
67
+ capabilityDetails,
68
+ options,
69
+ stdout,
70
+ color,
71
+ resolveWrapWidth,
72
+ writeWrappedItems,
73
+ normalizeRelativePosixPath,
74
+ formatPackageSubpathImport,
75
+ normalizePlacementOutlets,
76
+ normalizePlacementContributions,
77
+ shouldShowPackageExportTarget,
78
+ classifyExportedSymbols,
79
+ deriveProviderDisplayName
80
+ });
81
+ }
82
+ }
83
+
84
+ function isLocalPackageLockEntry(lockEntry = {}) {
85
+ const sourceType = String(lockEntry?.source?.type || "").trim();
86
+ return sourceType === "local-package" || sourceType === "app-local-package";
87
+ }
88
+
89
+ function resolveInstalledLocalPackageEntry(lock = {}, id = "") {
90
+ const normalizedId = String(id || "").trim();
91
+ if (!normalizedId) {
92
+ return null;
93
+ }
94
+
95
+ const installedPackages = lock?.installedPackages || {};
96
+ const directEntry = installedPackages[normalizedId];
97
+ if (directEntry && isLocalPackageLockEntry(directEntry)) {
98
+ return {
99
+ packageId: normalizedId,
100
+ lockEntry: directEntry
101
+ };
102
+ }
103
+
104
+ for (const [packageId, lockEntry] of Object.entries(installedPackages)) {
105
+ if (!isLocalPackageLockEntry(lockEntry)) {
106
+ continue;
107
+ }
108
+ const recordedPackageId = String(lockEntry?.packageId || "").trim();
109
+ if (recordedPackageId === normalizedId) {
110
+ return {
111
+ packageId,
112
+ lockEntry
113
+ };
114
+ }
115
+ }
116
+
117
+ return null;
118
+ }
119
+
120
+ async function assertInstalledLocalPackageIsDiscoverable({ appRoot, id }) {
121
+ const { lock } = await loadLockFile(appRoot);
122
+ const installedLocalPackage = resolveInstalledLocalPackageEntry(lock, id);
123
+ if (!installedLocalPackage) {
124
+ return;
125
+ }
126
+
127
+ const { packageId, lockEntry } = installedLocalPackage;
128
+ const source = lockEntry?.source || {};
129
+ const descriptorPath = String(source.descriptorPath || "").trim();
130
+ if (!descriptorPath) {
131
+ throw createCliError(
132
+ `Local package ${packageId} is recorded in .jskit/lock.json but has no descriptorPath.`
133
+ );
134
+ }
135
+
136
+ const absoluteDescriptorPath = path.resolve(appRoot, descriptorPath);
137
+ if (!(await fileExists(absoluteDescriptorPath))) {
138
+ throw createCliError(
139
+ `Local package ${packageId} is recorded in .jskit/lock.json but descriptor is missing at ${descriptorPath}.`
140
+ );
141
+ }
142
+
143
+ const packagePath = String(source.packagePath || "").trim();
144
+ if (packagePath) {
145
+ const packageJsonPath = path.join(appRoot, packagePath, "package.json");
146
+ if (!(await fileExists(packageJsonPath))) {
147
+ throw createCliError(
148
+ `Local package ${packageId} is recorded in .jskit/lock.json but package.json is missing at ${packagePath}/package.json.`
149
+ );
150
+ }
151
+ }
152
+
153
+ const descriptorLabel = normalizeRelativePath(appRoot, absoluteDescriptorPath);
154
+ throw createCliError(
155
+ `Local package ${packageId} is recorded in .jskit/lock.json but was not discoverable from ${descriptorLabel}. Ensure its package.json name matches the descriptor packageId.`
156
+ );
157
+ }
158
+
159
+ async function resolveAppLocalShowTarget({ id, cwd, catalogPackageRegistry }) {
160
+ let appRoot = "";
161
+ try {
162
+ appRoot = await resolveAppRootFromCwd(cwd);
163
+ } catch {
164
+ return null;
165
+ }
166
+
167
+ const appLocalRegistry = await loadAppLocalPackageRegistry(appRoot);
168
+ const resolvedPackageId = resolvePackageIdInput(id, appLocalRegistry);
169
+ if (resolvedPackageId) {
170
+ return {
171
+ packageRegistry: mergePackageRegistries(catalogPackageRegistry, appLocalRegistry),
172
+ packageEntry: appLocalRegistry.get(resolvedPackageId)
173
+ };
174
+ }
175
+
176
+ await assertInstalledLocalPackageIsDiscoverable({ appRoot, id });
177
+ return null;
178
+ }
179
+
180
+ async function commandShow({ positional, options, cwd, stdout }) {
32
181
  const id = String(positional[0] || "").trim();
33
182
  if (!id) {
34
183
  throw createCliError("show requires an id.", { showUsage: true });
@@ -41,43 +190,13 @@ function createShowCommand(ctx = {}) {
41
190
 
42
191
  if (resolvedPackageId) {
43
192
  const packageEntry = packageRegistry.get(resolvedPackageId);
44
- const {
45
- payload,
46
- provides,
47
- requires,
48
- capabilityDetails
49
- } = await buildPackageShowPayload({
193
+ await renderPackageEntry({
50
194
  packageRegistry,
51
195
  packageEntry,
52
196
  options,
53
- inspectPackageOfferings,
54
- buildFileWriteGroups,
55
- listDeclaredCapabilities,
56
- buildCapabilityDetailsForPackage
197
+ stdout,
198
+ color
57
199
  });
58
-
59
- if (options.json) {
60
- stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
61
- } else {
62
- renderPackagePayloadText({
63
- payload,
64
- provides,
65
- requires,
66
- capabilityDetails,
67
- options,
68
- stdout,
69
- color,
70
- resolveWrapWidth,
71
- writeWrappedItems,
72
- normalizeRelativePosixPath,
73
- formatPackageSubpathImport,
74
- normalizePlacementOutlets,
75
- normalizePlacementContributions,
76
- shouldShowPackageExportTarget,
77
- classifyExportedSymbols,
78
- deriveProviderDisplayName
79
- });
80
- }
81
200
  return 0;
82
201
  }
83
202
 
@@ -103,6 +222,22 @@ function createShowCommand(ctx = {}) {
103
222
  return 0;
104
223
  }
105
224
 
225
+ const appLocalTarget = await resolveAppLocalShowTarget({
226
+ id,
227
+ cwd,
228
+ catalogPackageRegistry: packageRegistry
229
+ });
230
+ if (appLocalTarget) {
231
+ await renderPackageEntry({
232
+ packageRegistry: appLocalTarget.packageRegistry,
233
+ packageEntry: appLocalTarget.packageEntry,
234
+ options,
235
+ stdout,
236
+ color
237
+ });
238
+ return 0;
239
+ }
240
+
106
241
  throw createCliError(`Unknown package or bundle: ${id}`);
107
242
  }
108
243
 
@@ -23,6 +23,8 @@ function parseArgs(argv, { createCliError } = {}) {
23
23
  verbose: false,
24
24
  json: false,
25
25
  all: false,
26
+ abandoned: false,
27
+ completed: false,
26
28
  concrete: false,
27
29
  help: true,
28
30
  inlineOptions: {}
@@ -50,6 +52,8 @@ function parseArgs(argv, { createCliError } = {}) {
50
52
  verbose: false,
51
53
  json: false,
52
54
  all: false,
55
+ abandoned: false,
56
+ completed: false,
53
57
  concrete: false,
54
58
  help: false,
55
59
  inlineOptions: {}
@@ -109,6 +113,14 @@ function parseArgs(argv, { createCliError } = {}) {
109
113
  options.all = true;
110
114
  continue;
111
115
  }
116
+ if (token === "--abandoned") {
117
+ options.abandoned = true;
118
+ continue;
119
+ }
120
+ if (token === "--completed") {
121
+ options.completed = true;
122
+ continue;
123
+ }
112
124
  if (token === "--concrete") {
113
125
  options.concrete = true;
114
126
  continue;
@@ -125,6 +137,14 @@ function parseArgs(argv, { createCliError } = {}) {
125
137
  options.inlineOptions.install = "true";
126
138
  continue;
127
139
  }
140
+ if (token === "--skip-ui-check") {
141
+ options.inlineOptions["skip-ui-check"] = "true";
142
+ continue;
143
+ }
144
+ if (token === "--close-without-merge") {
145
+ options.inlineOptions["close-without-merge"] = "true";
146
+ continue;
147
+ }
128
148
 
129
149
  if (token.startsWith("--")) {
130
150
  const withoutPrefix = token.slice(2);