planmode 0.2.2 → 0.3.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.
package/dist/mcp.js CHANGED
@@ -35,33 +35,33 @@ var logger = {
35
35
  return capturing;
36
36
  },
37
37
  info(msg) {
38
- const text = `info ${msg}`;
38
+ const text2 = `info ${msg}`;
39
39
  if (capturing) {
40
- captured.push(stripAnsi(text));
40
+ captured.push(stripAnsi(text2));
41
41
  } else {
42
42
  console.log(`${CYAN}info${RESET} ${msg}`);
43
43
  }
44
44
  },
45
45
  success(msg) {
46
- const text = `\u2713 ${msg}`;
46
+ const text2 = `\u2713 ${msg}`;
47
47
  if (capturing) {
48
- captured.push(stripAnsi(text));
48
+ captured.push(stripAnsi(text2));
49
49
  } else {
50
50
  console.log(`${GREEN}\u2713${RESET} ${msg}`);
51
51
  }
52
52
  },
53
53
  warn(msg) {
54
- const text = `warn ${msg}`;
54
+ const text2 = `warn ${msg}`;
55
55
  if (capturing) {
56
- captured.push(stripAnsi(text));
56
+ captured.push(stripAnsi(text2));
57
57
  } else {
58
58
  console.log(`${YELLOW}warn${RESET} ${msg}`);
59
59
  }
60
60
  },
61
61
  error(msg) {
62
- const text = `error ${msg}`;
62
+ const text2 = `error ${msg}`;
63
63
  if (capturing) {
64
- captured.push(stripAnsi(text));
64
+ captured.push(stripAnsi(text2));
65
65
  } else {
66
66
  console.error(`${RED}error${RESET} ${msg}`);
67
67
  }
@@ -660,6 +660,118 @@ function trackDownload(packageName) {
660
660
  });
661
661
  }
662
662
 
663
+ // src/lib/prompts.ts
664
+ import * as p from "@clack/prompts";
665
+ function isInteractive() {
666
+ return Boolean(process.stdin.isTTY) && !process.env.CI;
667
+ }
668
+ function handleCancel(value) {
669
+ if (p.isCancel(value)) {
670
+ p.cancel("Cancelled.");
671
+ process.exit(0);
672
+ }
673
+ return value;
674
+ }
675
+ async function promptForVariable(name, def) {
676
+ switch (def.type) {
677
+ case "enum": {
678
+ const value = await p.select({
679
+ message: def.description || name,
680
+ options: (def.options ?? []).map((opt) => ({
681
+ value: opt,
682
+ label: opt
683
+ })),
684
+ initialValue: def.default !== void 0 ? String(def.default) : void 0
685
+ });
686
+ return handleCancel(value);
687
+ }
688
+ case "boolean": {
689
+ const value = await p.confirm({
690
+ message: def.description || name,
691
+ initialValue: def.default !== void 0 ? Boolean(def.default) : false
692
+ });
693
+ return handleCancel(value);
694
+ }
695
+ case "number": {
696
+ const value = await p.text({
697
+ message: def.description || name,
698
+ placeholder: def.default !== void 0 ? String(def.default) : void 0,
699
+ defaultValue: def.default !== void 0 ? String(def.default) : void 0,
700
+ validate(input) {
701
+ if (isNaN(Number(input))) return "Must be a number";
702
+ }
703
+ });
704
+ return Number(handleCancel(value));
705
+ }
706
+ case "string":
707
+ default: {
708
+ const value = await p.text({
709
+ message: def.description || name,
710
+ placeholder: def.default !== void 0 ? String(def.default) : void 0,
711
+ defaultValue: def.default !== void 0 ? String(def.default) : void 0,
712
+ validate(input) {
713
+ if (def.required && !input) return `${name} is required`;
714
+ }
715
+ });
716
+ return handleCancel(value);
717
+ }
718
+ }
719
+ }
720
+ async function promptForVariables(variableDefs, provided, noInput = false) {
721
+ const values = {};
722
+ for (const [name, def] of Object.entries(variableDefs)) {
723
+ if (def.type === "resolved") continue;
724
+ if (provided[name] !== void 0) {
725
+ values[name] = coerceValue2(provided[name], def);
726
+ } else if (def.default !== void 0) {
727
+ if (isInteractive() && !noInput) {
728
+ values[name] = await promptForVariable(name, def);
729
+ } else {
730
+ values[name] = def.default;
731
+ }
732
+ } else if (def.required) {
733
+ if (isInteractive() && !noInput) {
734
+ values[name] = await promptForVariable(name, def);
735
+ } else {
736
+ throw new Error(`Missing required variable: ${name} -- ${def.description}`);
737
+ }
738
+ }
739
+ }
740
+ return values;
741
+ }
742
+ function coerceValue2(raw, def) {
743
+ switch (def.type) {
744
+ case "number":
745
+ return Number(raw);
746
+ case "boolean":
747
+ return raw === "true" || raw === "1" || raw === "yes";
748
+ case "enum":
749
+ if (def.options && !def.options.includes(raw)) {
750
+ throw new Error(
751
+ `Invalid value "${raw}" for enum variable. Options: ${def.options.join(", ")}`
752
+ );
753
+ }
754
+ return raw;
755
+ default:
756
+ return raw;
757
+ }
758
+ }
759
+ async function withSpinner(message, fn, successMessage) {
760
+ if (!isInteractive()) {
761
+ return fn();
762
+ }
763
+ const s = p.spinner();
764
+ s.start(message);
765
+ try {
766
+ const result = await fn();
767
+ s.stop(successMessage ?? message);
768
+ return result;
769
+ } catch (err) {
770
+ s.stop(`Failed: ${message}`);
771
+ throw err;
772
+ }
773
+ }
774
+
663
775
  // src/lib/installer.ts
664
776
  function getInstallDir(type) {
665
777
  switch (type) {
@@ -679,38 +791,60 @@ function contentHash(content) {
679
791
  }
680
792
  async function installPackage(packageName, options = {}) {
681
793
  const projectDir = options.projectDir ?? process.cwd();
794
+ const interactive = options.interactive ?? (isInteractive() && !options.noInput);
682
795
  const locked = getLockedVersion(packageName, projectDir);
683
796
  if (locked && !options.version) {
684
797
  logger.dim(`${packageName}@${locked.version} already installed`);
685
798
  return;
686
799
  }
687
- logger.info(`Resolving ${packageName}...`);
688
- const { version, metadata } = await resolveVersion(packageName, options.version);
689
- const versionMeta = await fetchVersionMetadata(packageName, version);
690
- logger.info(`Fetching ${packageName}@${version}...`);
691
- const basePath = versionMeta.source.path ? `${versionMeta.source.path}/` : "";
692
- const manifestRaw = await fetchFileAtTag(
693
- versionMeta.source.repository,
694
- versionMeta.source.tag,
695
- `${basePath}planmode.yaml`
696
- );
697
- const manifest = parseManifest(manifestRaw);
698
- let content;
699
- if (manifest.content) {
700
- content = manifest.content;
701
- } else if (manifest.content_file) {
702
- content = await fetchFileAtTag(
800
+ const resolveAndFetch = async () => {
801
+ const { version: version2, metadata: metadata2 } = await resolveVersion(packageName, options.version);
802
+ const versionMeta2 = await fetchVersionMetadata(packageName, version2);
803
+ return { version: version2, metadata: metadata2, versionMeta: versionMeta2 };
804
+ };
805
+ const { version, metadata, versionMeta } = interactive ? await withSpinner(
806
+ `Resolving ${packageName}...`,
807
+ resolveAndFetch,
808
+ `Resolved ${packageName}`
809
+ ) : await (async () => {
810
+ logger.info(`Resolving ${packageName}...`);
811
+ return resolveAndFetch();
812
+ })();
813
+ const fetchContent = async () => {
814
+ const basePath = versionMeta.source.path ? `${versionMeta.source.path}/` : "";
815
+ const manifestRaw = await fetchFileAtTag(
703
816
  versionMeta.source.repository,
704
817
  versionMeta.source.tag,
705
- `${basePath}${manifest.content_file}`
818
+ `${basePath}planmode.yaml`
706
819
  );
707
- } else {
708
- throw new Error("Package has no content or content_file");
709
- }
820
+ const manifest2 = parseManifest(manifestRaw);
821
+ let content2;
822
+ if (manifest2.content) {
823
+ content2 = manifest2.content;
824
+ } else if (manifest2.content_file) {
825
+ content2 = await fetchFileAtTag(
826
+ versionMeta.source.repository,
827
+ versionMeta.source.tag,
828
+ `${basePath}${manifest2.content_file}`
829
+ );
830
+ } else {
831
+ throw new Error("Package has no content or content_file");
832
+ }
833
+ return { manifest: manifest2, content: content2 };
834
+ };
835
+ const { manifest, content: rawContent } = interactive ? await withSpinner(
836
+ `Fetching ${packageName}@${version}...`,
837
+ fetchContent,
838
+ `Fetched ${packageName}@${version}`
839
+ ) : await (async () => {
840
+ logger.info(`Fetching ${packageName}@${version}...`);
841
+ return fetchContent();
842
+ })();
843
+ let content = rawContent;
710
844
  if (manifest.variables && Object.keys(manifest.variables).length > 0) {
711
845
  const provided = options.variables ?? {};
712
- if (options.noInput) {
713
- const values = collectVariableValues(manifest.variables, provided);
846
+ if (interactive) {
847
+ const values = await promptForVariables(manifest.variables, provided, false);
714
848
  content = renderTemplate(content, values);
715
849
  } else {
716
850
  const values = collectVariableValues(manifest.variables, provided);
@@ -772,7 +906,8 @@ async function installPackage(packageName, options = {}) {
772
906
  await installPackage(name, {
773
907
  version: range === "*" ? void 0 : range,
774
908
  projectDir,
775
- noInput: options.noInput
909
+ noInput: options.noInput,
910
+ interactive: options.interactive
776
911
  });
777
912
  }
778
913
  }
@@ -919,139 +1054,151 @@ function createPackage(options) {
919
1054
  // src/lib/publisher.ts
920
1055
  async function publishPackage(options = {}) {
921
1056
  const cwd = options.projectDir ?? process.cwd();
1057
+ const interactive = options.interactive ?? false;
922
1058
  const token = options.token ?? getGitHubToken();
923
1059
  if (!token) {
924
1060
  throw new Error("Not authenticated. Run `planmode login` first.");
925
1061
  }
926
- logger.info("Reading planmode.yaml...");
927
- const manifest = readManifest(cwd);
928
- const errors = validateManifest(manifest, true);
929
- if (errors.length > 0) {
930
- throw new Error(`Invalid manifest:
1062
+ const doValidate = async () => {
1063
+ const manifest2 = readManifest(cwd);
1064
+ const errors = validateManifest(manifest2, true);
1065
+ if (errors.length > 0) {
1066
+ throw new Error(`Invalid manifest:
931
1067
  ${errors.map((e) => ` - ${e}`).join("\n")}`);
932
- }
1068
+ }
1069
+ return manifest2;
1070
+ };
1071
+ const manifest = interactive ? await withSpinner("Validating manifest...", doValidate, "Manifest valid") : await (async () => {
1072
+ logger.info("Reading planmode.yaml...");
1073
+ return doValidate();
1074
+ })();
933
1075
  const remoteUrl = await getRemoteUrl(cwd);
934
1076
  if (!remoteUrl) {
935
1077
  throw new Error("No git remote found. Push your code to GitHub first.");
936
1078
  }
937
1079
  const sha = await getHeadSha(cwd);
938
1080
  const tag = `v${manifest.version}`;
939
- logger.info(`Creating tag ${tag}...`);
940
- try {
941
- await createTag(cwd, tag);
942
- } catch {
943
- logger.dim(`Tag ${tag} already exists, using existing`);
944
- }
945
- try {
946
- await pushTag(cwd, tag);
1081
+ const doTag = async () => {
1082
+ try {
1083
+ await createTag(cwd, tag);
1084
+ } catch {
1085
+ }
1086
+ try {
1087
+ await pushTag(cwd, tag);
1088
+ } catch {
1089
+ }
1090
+ };
1091
+ if (interactive) {
1092
+ await withSpinner(`Creating tag ${tag}...`, doTag, `Tag ${tag} ready`);
1093
+ } else {
1094
+ logger.info(`Creating tag ${tag}...`);
1095
+ await doTag();
947
1096
  logger.success(`Pushed tag ${tag}`);
948
- } catch {
949
- logger.dim(`Tag ${tag} already pushed`);
950
1097
  }
951
- logger.info("Submitting to registry...");
952
- const headers = {
953
- Authorization: `Bearer ${token}`,
954
- Accept: "application/vnd.github.v3+json",
955
- "User-Agent": "planmode-cli",
956
- "Content-Type": "application/json"
957
- };
958
- await fetch("https://api.github.com/repos/kaihannonen/planmode.org/forks", {
959
- method: "POST",
960
- headers
961
- });
962
- const userRes = await fetch("https://api.github.com/user", { headers });
963
- if (!userRes.ok) {
964
- throw new Error("Failed to authenticate with GitHub. Check your token.");
965
- }
966
- const user = await userRes.json();
967
- const repoPath = remoteUrl.replace(/^https?:\/\//, "").replace(/\.git$/, "");
968
- const metadataContent = JSON.stringify(
969
- {
970
- name: manifest.name,
971
- description: manifest.description,
972
- author: manifest.author,
973
- license: manifest.license,
974
- repository: repoPath,
975
- category: manifest.category ?? "other",
976
- tags: manifest.tags ?? [],
977
- type: manifest.type,
978
- models: manifest.models ?? [],
979
- latest_version: manifest.version,
980
- versions: [manifest.version],
981
- downloads: 0,
982
- created_at: (/* @__PURE__ */ new Date()).toISOString(),
983
- updated_at: (/* @__PURE__ */ new Date()).toISOString(),
984
- dependencies: manifest.dependencies,
985
- variables: manifest.variables
986
- },
987
- null,
988
- 2
989
- );
990
- const versionContent = JSON.stringify(
991
- {
992
- version: manifest.version,
993
- published_at: (/* @__PURE__ */ new Date()).toISOString(),
994
- source: {
1098
+ const doSubmit = async () => {
1099
+ const headers = {
1100
+ Authorization: `Bearer ${token}`,
1101
+ Accept: "application/vnd.github.v3+json",
1102
+ "User-Agent": "planmode-cli",
1103
+ "Content-Type": "application/json"
1104
+ };
1105
+ await fetch("https://api.github.com/repos/kaihannonen/planmode.org/forks", {
1106
+ method: "POST",
1107
+ headers
1108
+ });
1109
+ const userRes = await fetch("https://api.github.com/user", { headers });
1110
+ if (!userRes.ok) {
1111
+ throw new Error("Failed to authenticate with GitHub. Check your token.");
1112
+ }
1113
+ const user = await userRes.json();
1114
+ const repoPath = remoteUrl.replace(/^https?:\/\//, "").replace(/\.git$/, "");
1115
+ const metadataContent = JSON.stringify(
1116
+ {
1117
+ name: manifest.name,
1118
+ description: manifest.description,
1119
+ author: manifest.author,
1120
+ license: manifest.license,
995
1121
  repository: repoPath,
996
- tag,
997
- sha
1122
+ category: manifest.category ?? "other",
1123
+ tags: manifest.tags ?? [],
1124
+ type: manifest.type,
1125
+ models: manifest.models ?? [],
1126
+ latest_version: manifest.version,
1127
+ versions: [manifest.version],
1128
+ downloads: 0,
1129
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
1130
+ updated_at: (/* @__PURE__ */ new Date()).toISOString(),
1131
+ dependencies: manifest.dependencies,
1132
+ variables: manifest.variables
998
1133
  },
999
- files: ["planmode.yaml", manifest.content_file ?? "inline"],
1000
- content_hash: `sha256:${sha.slice(0, 16)}`
1001
- },
1002
- null,
1003
- 2
1004
- );
1005
- const branchName = `add-${manifest.name}-${manifest.version}`;
1006
- const refRes = await fetch(
1007
- `https://api.github.com/repos/${user.login}/planmode.org/git/ref/heads/main`,
1008
- { headers }
1009
- );
1010
- if (!refRes.ok) {
1011
- throw new Error("Failed to access registry fork. Make sure the fork exists.");
1012
- }
1013
- const refData = await refRes.json();
1014
- const baseSha = refData.object.sha;
1015
- await fetch(`https://api.github.com/repos/${user.login}/planmode.org/git/refs`, {
1016
- method: "POST",
1017
- headers,
1018
- body: JSON.stringify({
1019
- ref: `refs/heads/${branchName}`,
1020
- sha: baseSha
1021
- })
1022
- });
1023
- await fetch(
1024
- `https://api.github.com/repos/${user.login}/planmode.org/contents/registry/packages/${manifest.name}/metadata.json`,
1025
- {
1026
- method: "PUT",
1134
+ null,
1135
+ 2
1136
+ );
1137
+ const versionContent = JSON.stringify(
1138
+ {
1139
+ version: manifest.version,
1140
+ published_at: (/* @__PURE__ */ new Date()).toISOString(),
1141
+ source: {
1142
+ repository: repoPath,
1143
+ tag,
1144
+ sha
1145
+ },
1146
+ files: ["planmode.yaml", manifest.content_file ?? "inline"],
1147
+ content_hash: `sha256:${sha.slice(0, 16)}`
1148
+ },
1149
+ null,
1150
+ 2
1151
+ );
1152
+ const branchName = `add-${manifest.name}-${manifest.version}`;
1153
+ const refRes = await fetch(
1154
+ `https://api.github.com/repos/${user.login}/planmode.org/git/ref/heads/main`,
1155
+ { headers }
1156
+ );
1157
+ if (!refRes.ok) {
1158
+ throw new Error("Failed to access registry fork. Make sure the fork exists.");
1159
+ }
1160
+ const refData = await refRes.json();
1161
+ const baseSha = refData.object.sha;
1162
+ await fetch(`https://api.github.com/repos/${user.login}/planmode.org/git/refs`, {
1163
+ method: "POST",
1027
1164
  headers,
1028
1165
  body: JSON.stringify({
1029
- message: `Add ${manifest.name}@${manifest.version}`,
1030
- content: Buffer.from(metadataContent).toString("base64"),
1031
- branch: branchName
1166
+ ref: `refs/heads/${branchName}`,
1167
+ sha: baseSha
1032
1168
  })
1033
- }
1034
- );
1035
- await fetch(
1036
- `https://api.github.com/repos/${user.login}/planmode.org/contents/registry/packages/${manifest.name}/versions/${manifest.version}.json`,
1037
- {
1038
- method: "PUT",
1169
+ });
1170
+ await fetch(
1171
+ `https://api.github.com/repos/${user.login}/planmode.org/contents/registry/packages/${manifest.name}/metadata.json`,
1172
+ {
1173
+ method: "PUT",
1174
+ headers,
1175
+ body: JSON.stringify({
1176
+ message: `Add ${manifest.name}@${manifest.version}`,
1177
+ content: Buffer.from(metadataContent).toString("base64"),
1178
+ branch: branchName
1179
+ })
1180
+ }
1181
+ );
1182
+ await fetch(
1183
+ `https://api.github.com/repos/${user.login}/planmode.org/contents/registry/packages/${manifest.name}/versions/${manifest.version}.json`,
1184
+ {
1185
+ method: "PUT",
1186
+ headers,
1187
+ body: JSON.stringify({
1188
+ message: `Add ${manifest.name}@${manifest.version} version metadata`,
1189
+ content: Buffer.from(versionContent).toString("base64"),
1190
+ branch: branchName
1191
+ })
1192
+ }
1193
+ );
1194
+ const prRes = await fetch("https://api.github.com/repos/kaihannonen/planmode.org/pulls", {
1195
+ method: "POST",
1039
1196
  headers,
1040
1197
  body: JSON.stringify({
1041
- message: `Add ${manifest.name}@${manifest.version} version metadata`,
1042
- content: Buffer.from(versionContent).toString("base64"),
1043
- branch: branchName
1044
- })
1045
- }
1046
- );
1047
- const prRes = await fetch("https://api.github.com/repos/kaihannonen/planmode.org/pulls", {
1048
- method: "POST",
1049
- headers,
1050
- body: JSON.stringify({
1051
- title: `Add ${manifest.name}@${manifest.version}`,
1052
- head: `${user.login}:${branchName}`,
1053
- base: "main",
1054
- body: `## New package: ${manifest.name}
1198
+ title: `Add ${manifest.name}@${manifest.version}`,
1199
+ head: `${user.login}:${branchName}`,
1200
+ base: "main",
1201
+ body: `## New package: ${manifest.name}
1055
1202
 
1056
1203
  - **Type:** ${manifest.type}
1057
1204
  - **Version:** ${manifest.version}
@@ -1059,17 +1206,30 @@ ${errors.map((e) => ` - ${e}`).join("\n")}`);
1059
1206
  - **Author:** ${manifest.author}
1060
1207
 
1061
1208
  Submitted via \`planmode publish\`.`
1062
- })
1063
- });
1064
- if (!prRes.ok) {
1065
- const err = await prRes.text();
1066
- throw new Error(`Failed to create PR: ${err}`);
1209
+ })
1210
+ });
1211
+ if (!prRes.ok) {
1212
+ const err = await prRes.text();
1213
+ throw new Error(`Failed to create PR: ${err}`);
1214
+ }
1215
+ const pr = await prRes.json();
1216
+ return pr.html_url;
1217
+ };
1218
+ let prUrl;
1219
+ if (interactive) {
1220
+ prUrl = await withSpinner(
1221
+ "Submitting to registry...",
1222
+ doSubmit,
1223
+ "Submitted to registry"
1224
+ );
1225
+ } else {
1226
+ logger.info("Submitting to registry...");
1227
+ prUrl = await doSubmit();
1228
+ logger.success(`Published ${manifest.name}@${manifest.version}`);
1229
+ logger.info(`PR: ${prUrl}`);
1067
1230
  }
1068
- const pr = await prRes.json();
1069
- logger.success(`Published ${manifest.name}@${manifest.version}`);
1070
- logger.info(`PR: ${pr.html_url}`);
1071
1231
  return {
1072
- prUrl: pr.html_url,
1232
+ prUrl,
1073
1233
  packageName: manifest.name,
1074
1234
  version: manifest.version
1075
1235
  };
@@ -1714,9 +1874,9 @@ async function withCaptureAsync(fn) {
1714
1874
  throw Object.assign(err, { capturedMessages: messages });
1715
1875
  }
1716
1876
  }
1717
- function textResult(text, isError = false) {
1877
+ function textResult(text2, isError = false) {
1718
1878
  return {
1719
- content: [{ type: "text", text }],
1879
+ content: [{ type: "text", text: text2 }],
1720
1880
  isError
1721
1881
  };
1722
1882
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "planmode",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "description": "The open source package manager for AI plans, rules, and prompts.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -16,6 +16,7 @@
16
16
  "typecheck": "tsc --noEmit"
17
17
  },
18
18
  "dependencies": {
19
+ "@clack/prompts": "^0.9.1",
19
20
  "@modelcontextprotocol/sdk": "^1.26.0",
20
21
  "commander": "^13.1.0",
21
22
  "handlebars": "^4.7.8",
@@ -1,19 +1,35 @@
1
1
  import { Command } from "commander";
2
+ import * as p from "@clack/prompts";
2
3
  import { runDoctor } from "../lib/doctor.js";
3
4
  import { logger } from "../lib/logger.js";
5
+ import { isInteractive } from "../lib/prompts.js";
4
6
 
5
7
  export const doctorCommand = new Command("doctor")
6
8
  .description("Check project health: verify installed packages, imports, and file integrity")
7
9
  .action(() => {
10
+ const interactive = isInteractive();
8
11
  const result = runDoctor();
9
12
 
10
- logger.blank();
11
- logger.bold(`Checked ${result.packagesChecked} package(s)`);
12
- logger.blank();
13
+ if (interactive) {
14
+ p.intro("Health check");
15
+ } else {
16
+ logger.blank();
17
+ }
13
18
 
14
- if (result.issues.length === 0) {
15
- logger.success("Everything looks good. No issues found.");
19
+ if (interactive) {
20
+ p.log.info(`Checked ${result.packagesChecked} package(s)`);
21
+ } else {
22
+ logger.bold(`Checked ${result.packagesChecked} package(s)`);
16
23
  logger.blank();
24
+ }
25
+
26
+ if (result.issues.length === 0) {
27
+ if (interactive) {
28
+ p.outro("Everything looks good. No issues found.");
29
+ } else {
30
+ logger.success("Everything looks good. No issues found.");
31
+ logger.blank();
32
+ }
17
33
  return;
18
34
  }
19
35
 
@@ -21,21 +37,37 @@ export const doctorCommand = new Command("doctor")
21
37
  const warnings = result.issues.filter((i) => i.severity === "warning");
22
38
 
23
39
  for (const issue of errors) {
24
- logger.error(issue.message);
25
- if (issue.fix) logger.dim(` Fix: ${issue.fix}`);
40
+ if (interactive) {
41
+ p.log.error(issue.message);
42
+ } else {
43
+ logger.error(issue.message);
44
+ if (issue.fix) logger.dim(` Fix: ${issue.fix}`);
45
+ }
26
46
  }
27
47
  for (const issue of warnings) {
28
- logger.warn(issue.message);
29
- if (issue.fix) logger.dim(` Fix: ${issue.fix}`);
48
+ if (interactive) {
49
+ p.log.warn(issue.message);
50
+ } else {
51
+ logger.warn(issue.message);
52
+ if (issue.fix) logger.dim(` Fix: ${issue.fix}`);
53
+ }
30
54
  }
31
55
 
32
- logger.blank();
33
- if (errors.length > 0) {
34
- logger.error(`${errors.length} error(s), ${warnings.length} warning(s)`);
56
+ if (interactive) {
57
+ if (errors.length > 0) {
58
+ p.outro(`${errors.length} error(s), ${warnings.length} warning(s)`);
59
+ } else {
60
+ p.outro(`${warnings.length} warning(s)`);
61
+ }
35
62
  } else {
36
- logger.warn(`${warnings.length} warning(s)`);
63
+ logger.blank();
64
+ if (errors.length > 0) {
65
+ logger.error(`${errors.length} error(s), ${warnings.length} warning(s)`);
66
+ } else {
67
+ logger.warn(`${warnings.length} warning(s)`);
68
+ }
69
+ logger.blank();
37
70
  }
38
- logger.blank();
39
71
 
40
72
  if (errors.length > 0) {
41
73
  process.exit(1);