@dadado/agent-kit-cli 5.15.1 → 5.17.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.
Files changed (72) hide show
  1. package/dashboard/dashboard-data.mjs +69 -65
  2. package/dashboard/lib/guards.d.mts +5 -0
  3. package/dashboard/lib/guards.mjs +31 -0
  4. package/dashboard/lib/identity-probe.mjs +56 -0
  5. package/dashboard/lib/pipeline-cache.mjs +58 -0
  6. package/dashboard/lib/processes.mjs +71 -0
  7. package/dashboard/serve.mjs +43 -1
  8. package/dashboard/start-broadcast.mjs +2 -12
  9. package/dashboard/start.mjs +2 -13
  10. package/dist/add-HTYIFKMN.js +150 -0
  11. package/dist/chunk-2IPXQFJE.js +253 -0
  12. package/dist/chunk-2RDRMGD3.js +82 -0
  13. package/dist/chunk-2YU2H7I6.js +42 -0
  14. package/dist/chunk-4IWG5CVE.js +214 -0
  15. package/dist/chunk-5VCOWTTP.js +6 -0
  16. package/dist/chunk-77V75A7O.js +39 -0
  17. package/dist/chunk-7HDQEK6T.js +26 -0
  18. package/dist/chunk-AW74INUW.js +1456 -0
  19. package/dist/chunk-B73KQ5KX.js +972 -0
  20. package/dist/chunk-CBCW5R5J.js +484 -0
  21. package/dist/chunk-DE3LQFSJ.js +18 -0
  22. package/dist/chunk-DQA434Y4.js +106 -0
  23. package/dist/chunk-F4VMYR7U.js +318 -0
  24. package/dist/chunk-F6C3GFKV.js +57 -0
  25. package/dist/chunk-HFUH3HVS.js +282 -0
  26. package/dist/chunk-HS7LVVLO.js +74 -0
  27. package/dist/chunk-IHROBTNM.js +135 -0
  28. package/dist/chunk-KD2MYBB6.js +213 -0
  29. package/dist/chunk-MAPOQEA2.js +43 -0
  30. package/dist/chunk-N3HTJVBR.js +341 -0
  31. package/dist/chunk-NQ7IQ5XT.js +26 -0
  32. package/dist/chunk-OUQZC744.js +1315 -0
  33. package/dist/chunk-PCCQUWE5.js +817 -0
  34. package/dist/chunk-PNFF6OU3.js +273 -0
  35. package/dist/chunk-PNG7LO2F.js +53 -0
  36. package/dist/chunk-QTON2GQX.js +203 -0
  37. package/dist/chunk-S6GMFDKV.js +50 -0
  38. package/dist/chunk-SMGFN3Z4.js +503 -0
  39. package/dist/chunk-STGFBAYT.js +641 -0
  40. package/dist/chunk-TFZ2JUXH.js +1081 -0
  41. package/dist/chunk-VY7MTPJ2.js +205 -0
  42. package/dist/chunk-W24CMQ32.js +525 -0
  43. package/dist/chunk-WW3YZKHW.js +165 -0
  44. package/dist/chunk-ZB43YMHW.js +20 -0
  45. package/dist/chunk-ZCTSUQRR.js +874 -0
  46. package/dist/chunk-ZXKZBTXE.js +17 -0
  47. package/dist/contribute-C6AW2P6J.js +367 -0
  48. package/dist/cursor-awareness-KTL2RGKD.js +77 -0
  49. package/dist/dashboard-YSNUGBFY.js +21 -0
  50. package/dist/dashboard-broadcast-M4CMW7RX.js +119 -0
  51. package/dist/diff-VHNR62SI.js +74 -0
  52. package/dist/doctor-PET2HRRJ.js +236 -0
  53. package/dist/dogfood-KKFVRWT6.js +366 -0
  54. package/dist/guard-EHXF76ID.js +530 -0
  55. package/dist/handoff-3UI6VKVG.js +302 -0
  56. package/dist/hook-DYIMKJYL.js +1074 -0
  57. package/dist/index.js +62 -14068
  58. package/dist/init-Q4LBM6KT.js +127 -0
  59. package/dist/install-RHOS3OYZ.js +34 -0
  60. package/dist/mission-control-LJACEVVN.js +89 -0
  61. package/dist/monitors-QV2IS3MP.js +176 -0
  62. package/dist/plan-index-JMC7Z4DW.js +44 -0
  63. package/dist/public-inbound-radar-WP3XF7GS.js +68 -0
  64. package/dist/remote-issues-SZ4HDQL5.js +44 -0
  65. package/dist/run-I3BFGULT.js +243 -0
  66. package/dist/run-plan-FO7CL4M3.js +118 -0
  67. package/dist/scan-NI2SWJ2E.js +33 -0
  68. package/dist/setup-global-M42GEODI.js +340 -0
  69. package/dist/status-XPVQVQMX.js +150 -0
  70. package/dist/update-FJ2EBZVO.js +283 -0
  71. package/dist/validate-V2CW7J4B.js +201 -0
  72. package/package.json +3 -3
@@ -0,0 +1,273 @@
1
+ // src/scanner/readiness.ts
2
+ import { createHash } from "crypto";
3
+ function action(id, status, recommendation, owner) {
4
+ return { id, status, recommendation, owner };
5
+ }
6
+ function check(id, title, status, essential, evidence, actions = []) {
7
+ return { id, title, status, essential, evidence, actions };
8
+ }
9
+ function pillar(pillarName, checks) {
10
+ return { pillar: pillarName, checks };
11
+ }
12
+ function repositoryFingerprint(scan) {
13
+ const fingerprintInput = {
14
+ purpose: scan.purpose,
15
+ stack: {
16
+ language: scan.stack.language,
17
+ framework: scan.stack.framework,
18
+ packageManager: scan.stack.packageManager,
19
+ workspaces: scan.stack.workspaces
20
+ },
21
+ git: {
22
+ mode: scan.git.mode,
23
+ remoteUrl: scan.git.remoteUrl,
24
+ currentBranch: scan.git.currentBranch,
25
+ defaultBranch: scan.git.defaultBranch
26
+ },
27
+ context: scan.context.sources,
28
+ agentKitVersion: scan.agentKit.version
29
+ };
30
+ return createHash("sha256").update(JSON.stringify(fingerprintInput)).digest("hex");
31
+ }
32
+ function buildPillars(scan) {
33
+ const agentKitStatus = scan.agentKit.installed ? "ready" : "auto_fix";
34
+ const purposeStatus = scan.purpose.value === "unknown" ? "needs_choice" : "ready";
35
+ const gitStatus = scan.git.mode === "none" ? "needs_choice" : "ready";
36
+ const secretsStatus = scan.safety.trackedSensitiveFiles.length > 0 ? "blocked" : scan.safety.trackedFilesUnknown ? "manual" : scan.safety.missingSecretPatterns.length > 0 ? "auto_fix" : "ready";
37
+ const contextStatus = scan.context.sources.length > 0 ? "ready" : "needs_choice";
38
+ const providerStatus = scan.git.mode === "none" ? "needs_choice" : scan.git.mode === "local-only" ? "ready" : scan.git.providerKind === "unknown" || scan.git.providerKind === "custom" && scan.git.providerConfidence === "low" ? "needs_choice" : "ready";
39
+ return [
40
+ pillar("workspace", [
41
+ check(
42
+ "workspace.agent-kit",
43
+ "Agent Kit installation",
44
+ agentKitStatus,
45
+ true,
46
+ scan.agentKit.manifestPath ? [{ source: "file", value: scan.agentKit.manifestPath }] : [],
47
+ agentKitStatus === "ready" ? [] : [action("install-agent-kit", "auto_fix", "Install the managed L0 inventory", "system")]
48
+ )
49
+ ]),
50
+ pillar("purpose-context", [
51
+ check(
52
+ "purpose.classification",
53
+ "Repository purpose",
54
+ purposeStatus,
55
+ true,
56
+ scan.purpose.evidence,
57
+ purposeStatus === "ready" ? [] : [
58
+ action(
59
+ "confirm-repository-purpose",
60
+ "needs_choice",
61
+ "Confirm the stable repository purpose",
62
+ "user"
63
+ )
64
+ ]
65
+ ),
66
+ check(
67
+ "context.sources",
68
+ "Project context sources",
69
+ contextStatus,
70
+ true,
71
+ scan.context.sources,
72
+ contextStatus === "ready" ? [] : [
73
+ action(
74
+ "identify-context-source",
75
+ "needs_choice",
76
+ "Identify the repository source of truth",
77
+ "user"
78
+ )
79
+ ]
80
+ )
81
+ ]),
82
+ pillar("source-control", [
83
+ check(
84
+ "git.repository",
85
+ "Git repository",
86
+ gitStatus,
87
+ true,
88
+ scan.git.currentBranch ? [{ source: "git", value: `branch:${scan.git.currentBranch}` }] : [],
89
+ gitStatus === "ready" ? [] : [
90
+ action(
91
+ "choose-source-control",
92
+ "needs_choice",
93
+ "Choose whether to initialize Git",
94
+ "user"
95
+ )
96
+ ]
97
+ ),
98
+ check(
99
+ "git.staging-branch",
100
+ "Staging branch",
101
+ scan.git.hasLocalStaging || scan.git.hasRemoteStaging ? "ready" : "needs_choice",
102
+ false,
103
+ [
104
+ ...scan.git.hasLocalStaging ? [{ source: "git", value: "local branch:staging" }] : [],
105
+ ...scan.git.hasRemoteStaging ? [{ source: "git", value: "remote branch:staging" }] : []
106
+ ],
107
+ scan.git.hasLocalStaging || scan.git.hasRemoteStaging ? [] : [
108
+ action(
109
+ "choose-branch-strategy",
110
+ "needs_choice",
111
+ "Confirm the repository promotion strategy",
112
+ "user"
113
+ )
114
+ ]
115
+ )
116
+ ]),
117
+ pillar("safety", [
118
+ check(
119
+ "safety.secrets",
120
+ "Secrets hygiene",
121
+ secretsStatus,
122
+ true,
123
+ [
124
+ ...scan.safety.ignoredSecretPatterns.map((value) => ({
125
+ source: "file",
126
+ value: `.gitignore:${value}`
127
+ })),
128
+ ...scan.safety.trackedSensitiveFiles.map((value) => ({
129
+ source: "git",
130
+ value: `tracked:${value}`
131
+ }))
132
+ ],
133
+ secretsStatus === "ready" ? [] : secretsStatus === "blocked" ? [
134
+ action(
135
+ "remove-tracked-secrets",
136
+ "blocked",
137
+ "Remove tracked sensitive files and rotate exposed values",
138
+ "user"
139
+ )
140
+ ] : secretsStatus === "manual" ? [
141
+ action(
142
+ "verify-tracked-secrets",
143
+ "manual",
144
+ "Tracked files could not be listed (git ls-files failed); check for tracked secrets manually",
145
+ "user"
146
+ ),
147
+ ...scan.safety.missingSecretPatterns.length > 0 ? [
148
+ action(
149
+ "merge-secret-ignores",
150
+ "auto_fix",
151
+ "Merge required secret patterns into .gitignore",
152
+ "system"
153
+ )
154
+ ] : []
155
+ ] : [
156
+ action(
157
+ "merge-secret-ignores",
158
+ "auto_fix",
159
+ "Merge required secret patterns into .gitignore",
160
+ "system"
161
+ )
162
+ ]
163
+ )
164
+ ]),
165
+ pillar("stack-tooling", [
166
+ check(
167
+ "stack.detected",
168
+ "Stack and package manager",
169
+ scan.stack.language !== "unknown" || scan.stack.hasProjectFiles ? "ready" : "needs_choice",
170
+ true,
171
+ scan.stack.packageManagerEvidence ?? []
172
+ )
173
+ ]),
174
+ pillar("quality-ci", [
175
+ (() => {
176
+ const hasValidation = scan.quality.hasTests || scan.quality.validationCommands.length > 0 || scan.quality.ciRunCommands.length > 0;
177
+ return check(
178
+ "quality.validation",
179
+ "Tests and validation",
180
+ hasValidation ? "ready" : "manual",
181
+ false,
182
+ [
183
+ ...scan.infra.ciFiles.map((value) => ({ source: "file", value })),
184
+ ...scan.quality.ciRunCommands.map((value) => ({
185
+ source: "file",
186
+ value: `ci:${value}`
187
+ }))
188
+ ],
189
+ hasValidation ? [] : [
190
+ action(
191
+ "document-validation",
192
+ "manual",
193
+ "Document a repeatable repository validation command",
194
+ "user"
195
+ )
196
+ ]
197
+ );
198
+ })()
199
+ ]),
200
+ pillar("deploy-infrastructure", [
201
+ check(
202
+ "infrastructure.detected",
203
+ "Deployment and infrastructure evidence",
204
+ "ready",
205
+ false,
206
+ [...scan.infra.infrastructureFiles, ...scan.infra.deploymentFiles].map((value) => ({
207
+ source: "file",
208
+ value
209
+ }))
210
+ )
211
+ ]),
212
+ pillar("collaboration", [
213
+ check(
214
+ "collaboration.provider",
215
+ "Repository provider",
216
+ providerStatus,
217
+ false,
218
+ scan.git.providerEvidence,
219
+ providerStatus === "ready" ? [] : [
220
+ action(
221
+ "confirm-provider",
222
+ "needs_choice",
223
+ "Confirm the remote provider or local-only model",
224
+ "user"
225
+ )
226
+ ]
227
+ )
228
+ ]),
229
+ pillar("agent-kit-personalization", [
230
+ check(
231
+ "agent-kit.context",
232
+ "Agent guidance",
233
+ scan.context.hasAgentGuidance ? "ready" : "auto_fix",
234
+ false,
235
+ scan.context.sources.filter((item) => item.value.includes("guidance")),
236
+ scan.context.hasAgentGuidance ? [] : [
237
+ action(
238
+ "prepare-agent-context",
239
+ "auto_fix",
240
+ "Prepare Agent Kit-owned context from detected evidence",
241
+ "system"
242
+ )
243
+ ]
244
+ )
245
+ ])
246
+ ];
247
+ }
248
+ function createReadinessReport(scan, options) {
249
+ const pillars = buildPillars(scan);
250
+ const checks = pillars.flatMap((item) => item.checks);
251
+ const statuses = ["ready", "auto_fix", "needs_choice", "manual", "blocked"];
252
+ const summary = Object.fromEntries(
253
+ statuses.map((status) => [status, checks.filter((item) => item.status === status).length])
254
+ );
255
+ const pendingActions = checks.flatMap((item) => item.actions).filter((item) => item.status !== "ready");
256
+ const { rootDir: _rootDir, ...portableScan } = scan;
257
+ return {
258
+ schemaVersion: 1,
259
+ generatorVersion: options.generatorVersion,
260
+ generatedAt: options.generatedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
261
+ repositoryFingerprint: repositoryFingerprint(scan),
262
+ summary,
263
+ scan: portableScan,
264
+ pillars,
265
+ appliedSafeFixes: [],
266
+ pendingActions,
267
+ deferredChecks: []
268
+ };
269
+ }
270
+
271
+ export {
272
+ createReadinessReport
273
+ };
@@ -0,0 +1,53 @@
1
+ import {
2
+ logger
3
+ } from "./chunk-ZB43YMHW.js";
4
+ import {
5
+ DEFAULT_REGISTRY_REF,
6
+ DEFAULT_REGISTRY_URL,
7
+ resolveRegistryRoot
8
+ } from "./chunk-AW74INUW.js";
9
+
10
+ // src/lifecycle/resolve-cli.ts
11
+ async function resolveRegistryFromCli(options) {
12
+ const fromManifest = options.manifest?.registry;
13
+ if (!options.registry && !options.url && fromManifest?.url) {
14
+ const ref = options.ref ?? fromManifest.ref ?? DEFAULT_REGISTRY_REF;
15
+ logger.info(`registry: ${fromManifest.url}@${ref} (from manifest)`);
16
+ if (fromManifest.url !== DEFAULT_REGISTRY_URL) {
17
+ logger.warn(
18
+ `registry url from .cursor/agent-kit.json is not the default ${DEFAULT_REGISTRY_URL}; verify you trust ${fromManifest.url}`
19
+ );
20
+ }
21
+ }
22
+ return resolveRegistryRoot({
23
+ cwd: options.cwd,
24
+ registryPath: options.registry,
25
+ registryUrl: options.url ?? fromManifest?.url,
26
+ registryRef: options.ref ?? fromManifest?.ref,
27
+ refresh: options.refresh
28
+ });
29
+ }
30
+ var REGISTRY_CLI_ARGS = {
31
+ registry: {
32
+ type: "string",
33
+ description: "Local path to a kit checkout that contains registry/"
34
+ },
35
+ url: {
36
+ type: "string",
37
+ description: "Remote registry git URL (default: public agent-kit)"
38
+ },
39
+ ref: {
40
+ type: "string",
41
+ description: "Git ref for remote registry (branch or tag)"
42
+ },
43
+ refresh: {
44
+ type: "boolean",
45
+ description: "Refresh cached remote registry",
46
+ default: false
47
+ }
48
+ };
49
+
50
+ export {
51
+ resolveRegistryFromCli,
52
+ REGISTRY_CLI_ARGS
53
+ };
@@ -0,0 +1,203 @@
1
+ // src/welcome/visual-kit.ts
2
+ import { cyan, gray, options as koloristOptions } from "kolorist";
3
+ var KOLORIST_TRUECOLOR = 3;
4
+ var HELMET_OUTLINE = "#e2e8f0";
5
+ var HELMET_FILL = "#0C8DEB";
6
+ var HELMET_ACCENT = "#00D0E7";
7
+ var LABEL_MUTED = "#8899aa";
8
+ var STATUS_OK = "#22c55e";
9
+ var STATUS_WARN = "#eab308";
10
+ var STATUS_ERR = "#ef4444";
11
+ var STATUS_INFO = "#06b6d4";
12
+ var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
13
+ var SPACE_MARKS = {
14
+ star: "\u2726",
15
+ tick: "\xB7",
16
+ diamond: "\u2727",
17
+ visor: "( )"
18
+ };
19
+ var KIT_TIPS = [
20
+ "Try agent-kit doctor for repository readiness.",
21
+ "HITL Ask UI is Cursor-only; agent-kit run uses a numbered list.",
22
+ "/run-plan never promotes to production.",
23
+ "Mission Control is the dashboard, not the CLI name.",
24
+ "NO_COLOR and CI keep this output static.",
25
+ "agent-kit --help groups SETUP, MISSION, DASHBOARD, INTEGRITY."
26
+ ];
27
+ function shouldUseWelcomeColor(opts = {}) {
28
+ const env = opts.env ?? process.env;
29
+ if (opts.color === false) return false;
30
+ if (env.NO_COLOR) return false;
31
+ if (env.NODE_DISABLE_COLORS) return false;
32
+ if (env.FORCE_COLOR === "0") return false;
33
+ if (opts.color === true) return true;
34
+ if (env.CI != null && env.CI !== "" && env.CI !== "false" && env.CI !== "0") return false;
35
+ const tty = opts.stdoutIsTTY ?? Boolean(process.stdout.isTTY);
36
+ return tty;
37
+ }
38
+ function withKoloristColor(fn) {
39
+ const prevEnabled = koloristOptions.enabled;
40
+ const prevLevel = koloristOptions.supportLevel;
41
+ koloristOptions.enabled = true;
42
+ koloristOptions.supportLevel = KOLORIST_TRUECOLOR;
43
+ try {
44
+ return fn();
45
+ } finally {
46
+ koloristOptions.enabled = prevEnabled;
47
+ koloristOptions.supportLevel = prevLevel;
48
+ }
49
+ }
50
+ function shouldUseVisualMotion(opts = {}) {
51
+ if (process.env.AGENT_KIT_REDUCED_MOTION === "1") return false;
52
+ return shouldUseWelcomeColor(opts);
53
+ }
54
+ function spinnerFrame(index) {
55
+ const n = SPINNER_FRAMES.length;
56
+ return SPINNER_FRAMES[(index % n + n) % n] ?? SPINNER_FRAMES[0];
57
+ }
58
+ function tipAt(index) {
59
+ const n = KIT_TIPS.length;
60
+ return KIT_TIPS[(index % n + n) % n] ?? KIT_TIPS[0];
61
+ }
62
+ function wrapNarrow(text, columns) {
63
+ const width = Math.max(8, Number.isFinite(columns) ? Math.floor(columns) : 80);
64
+ const words = text.trim().split(/\s+/).filter(Boolean);
65
+ if (words.length === 0) return "";
66
+ const lines = [];
67
+ let current = "";
68
+ for (const word of words) {
69
+ if (word.length > width) {
70
+ if (current) {
71
+ lines.push(current);
72
+ current = "";
73
+ }
74
+ for (let i = 0; i < word.length; i += width) {
75
+ const chunk = word.slice(i, i + width);
76
+ if (i + width >= word.length) current = chunk;
77
+ else lines.push(chunk);
78
+ }
79
+ continue;
80
+ }
81
+ const next = current ? `${current} ${word}` : word;
82
+ if (next.length > width) {
83
+ lines.push(current);
84
+ current = word;
85
+ } else {
86
+ current = next;
87
+ }
88
+ }
89
+ if (current) lines.push(current);
90
+ return lines.join("\n");
91
+ }
92
+ function renderProgressLine(opts) {
93
+ const motion = opts.motion ?? shouldUseVisualMotion();
94
+ const color = opts.color ?? shouldUseWelcomeColor();
95
+ const mark = motion ? spinnerFrame(opts.frameIndex) : SPACE_MARKS.tick;
96
+ const tip = tipAt(opts.tipIndex);
97
+ const label = opts.label?.trim() ?? "";
98
+ const raw = label ? `${mark} ${label} ${SPACE_MARKS.star} ${tip}` : `${mark} ${tip}`;
99
+ const cols = opts.columns ?? process.stdout.columns ?? 80;
100
+ const wrapped = wrapNarrow(raw, cols);
101
+ if (!color) return wrapped;
102
+ const prevEnabled = koloristOptions.enabled;
103
+ koloristOptions.enabled = true;
104
+ try {
105
+ return wrapped.split("\n").map((line, i) => i === 0 ? cyan(line) : gray(line)).join("\n");
106
+ } finally {
107
+ koloristOptions.enabled = prevEnabled;
108
+ }
109
+ }
110
+ var TtySpinner = class {
111
+ timer = null;
112
+ frame = 0;
113
+ startedAt = 0;
114
+ label = "";
115
+ write;
116
+ columns;
117
+ motion;
118
+ color;
119
+ intervalMs;
120
+ now;
121
+ constructor(opts = {}) {
122
+ this.write = opts.write ?? ((chunk) => process.stdout.write(chunk));
123
+ this.columns = opts.columns ?? (() => process.stdout.columns ?? 80);
124
+ this.motion = opts.motion ?? shouldUseVisualMotion();
125
+ this.color = opts.color ?? shouldUseWelcomeColor();
126
+ this.intervalMs = opts.intervalMs ?? 80;
127
+ this.now = opts.now ?? (() => Date.now());
128
+ }
129
+ start(label = "") {
130
+ this.stop();
131
+ this.label = label;
132
+ this.frame = 0;
133
+ this.startedAt = this.now();
134
+ this.paint(false);
135
+ if (!this.motion) return;
136
+ this.timer = setInterval(() => {
137
+ this.frame += 1;
138
+ this.paint(true);
139
+ }, this.intervalMs);
140
+ this.timer.unref?.();
141
+ }
142
+ stop(finalLine) {
143
+ if (this.timer) {
144
+ clearInterval(this.timer);
145
+ this.timer = null;
146
+ }
147
+ if (this.motion) this.write("\r\x1B[K");
148
+ if (finalLine) this.write(`${finalLine}
149
+ `);
150
+ }
151
+ paint(overwrite) {
152
+ const elapsed = this.now() - this.startedAt;
153
+ const tipIndex = Math.floor(elapsed / 4e3);
154
+ const line = renderProgressLine({
155
+ frameIndex: this.frame,
156
+ tipIndex,
157
+ columns: this.columns(),
158
+ label: this.label,
159
+ motion: this.motion,
160
+ color: this.color
161
+ }).split("\n")[0] ?? "";
162
+ if (overwrite && this.motion) this.write(`\r\x1B[K${line}`);
163
+ else if (this.motion) this.write(line);
164
+ else this.write(`${line}
165
+ `);
166
+ }
167
+ };
168
+ async function withCliProgress(label, fn, opts) {
169
+ const motion = opts?.motion ?? shouldUseVisualMotion();
170
+ if (!motion) {
171
+ return fn();
172
+ }
173
+ const spinner = new TtySpinner({ ...opts, motion: true });
174
+ spinner.start(label);
175
+ try {
176
+ const result = await fn();
177
+ spinner.stop();
178
+ return result;
179
+ } catch (err) {
180
+ spinner.stop();
181
+ throw err;
182
+ }
183
+ }
184
+
185
+ export {
186
+ HELMET_OUTLINE,
187
+ HELMET_FILL,
188
+ HELMET_ACCENT,
189
+ LABEL_MUTED,
190
+ STATUS_OK,
191
+ STATUS_WARN,
192
+ STATUS_ERR,
193
+ STATUS_INFO,
194
+ SPACE_MARKS,
195
+ shouldUseWelcomeColor,
196
+ withKoloristColor,
197
+ shouldUseVisualMotion,
198
+ spinnerFrame,
199
+ tipAt,
200
+ wrapNarrow,
201
+ TtySpinner,
202
+ withCliProgress
203
+ };
@@ -0,0 +1,50 @@
1
+ import {
2
+ MANIFEST_RELATIVE_PATH
3
+ } from "./chunk-AW74INUW.js";
4
+ import {
5
+ fileExists
6
+ } from "./chunk-77V75A7O.js";
7
+
8
+ // src/scanner/workspace-parent.ts
9
+ import { readdir } from "fs/promises";
10
+ import path from "path";
11
+ var MAX_CHILDREN_INSPECTED = 200;
12
+ var MAX_KIT_PROJECTS_REPORTED = 10;
13
+ async function detectWorkspaceParent(rootDir) {
14
+ try {
15
+ if (await fileExists(path.join(rootDir, MANIFEST_RELATIVE_PATH))) return null;
16
+ const entries = await readdir(rootDir, { withFileTypes: true });
17
+ const children = entries.filter(
18
+ (entry) => entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules"
19
+ ).map((entry) => entry.name).sort().slice(0, MAX_CHILDREN_INSPECTED);
20
+ const hits = await Promise.all(
21
+ children.map(
22
+ async (name) => await fileExists(path.join(rootDir, name, MANIFEST_RELATIVE_PATH)) ? name : null
23
+ )
24
+ );
25
+ const kitProjects = hits.filter((name) => name !== null).slice(0, MAX_KIT_PROJECTS_REPORTED);
26
+ return kitProjects.length > 0 ? { kitProjects } : null;
27
+ } catch {
28
+ return null;
29
+ }
30
+ }
31
+ function shellArg(value) {
32
+ return /^[\w@%+=:,./-]+$/.test(value) ? value : `'${value.replace(/'/g, "'\\''")}'`;
33
+ }
34
+ function workspaceParentNextStep(detection, command) {
35
+ return `cd ${shellArg(detection.kitProjects[0] ?? "<project>")} && agent-kit ${command}`;
36
+ }
37
+ function workspaceParentAdvisoryLines(rootDir, detection, command) {
38
+ return [
39
+ `No Agent Kit here: ${rootDir} is a workspace folder, not a kit project.`,
40
+ "Kit projects in its direct subfolders:",
41
+ ...detection.kitProjects.map((name) => ` - ${name}`),
42
+ `Run \`agent-kit ${command}\` from inside one of them.`,
43
+ `Next: ${workspaceParentNextStep(detection, command)}`
44
+ ];
45
+ }
46
+
47
+ export {
48
+ detectWorkspaceParent,
49
+ workspaceParentAdvisoryLines
50
+ };