vibefast-cli 0.1.3 → 0.2.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 (78) hide show
  1. package/AUTO-DETECT-DEPS.md +607 -0
  2. package/CHANGELOG.md +86 -0
  3. package/FINAL-PACKAGE-STRATEGY.md +583 -0
  4. package/FINAL-SIMPLE-PLAN.md +487 -0
  5. package/FLOW-DIAGRAM.md +1629 -0
  6. package/GOTCHAS-AND-RISKS.md +801 -0
  7. package/IMPLEMENTATION-COMPLETE.md +477 -0
  8. package/IMPLEMENTATION-PLAN.md +1360 -0
  9. package/PRE-PUBLISH-CHECKLIST.md +558 -0
  10. package/PRODUCTION-READINESS.md +684 -0
  11. package/PRODUCTION-TEST-RESULTS.md +465 -0
  12. package/README.md +73 -7
  13. package/READY-TO-PUBLISH.md +419 -0
  14. package/SIMPLIFIED-PLAN.md +578 -0
  15. package/TEST-SUMMARY.md +261 -0
  16. package/USER-MODIFICATIONS.md +448 -0
  17. package/cloudflare-worker/worker.js +39 -11
  18. package/dist/commands/add.d.ts.map +1 -1
  19. package/dist/commands/add.js +192 -15
  20. package/dist/commands/add.js.map +1 -1
  21. package/dist/commands/checklist.d.ts +3 -0
  22. package/dist/commands/checklist.d.ts.map +1 -0
  23. package/dist/commands/checklist.js +64 -0
  24. package/dist/commands/checklist.js.map +1 -0
  25. package/dist/commands/remove.d.ts.map +1 -1
  26. package/dist/commands/remove.js +85 -2
  27. package/dist/commands/remove.js.map +1 -1
  28. package/dist/commands/status.d.ts +3 -0
  29. package/dist/commands/status.d.ts.map +1 -0
  30. package/dist/commands/status.js +40 -0
  31. package/dist/commands/status.js.map +1 -0
  32. package/dist/core/__tests__/fsx.test.d.ts +2 -0
  33. package/dist/core/__tests__/fsx.test.d.ts.map +1 -0
  34. package/dist/core/__tests__/fsx.test.js +79 -0
  35. package/dist/core/__tests__/fsx.test.js.map +1 -0
  36. package/dist/core/__tests__/hash.test.d.ts +2 -0
  37. package/dist/core/__tests__/hash.test.d.ts.map +1 -0
  38. package/dist/core/__tests__/hash.test.js +84 -0
  39. package/dist/core/__tests__/hash.test.js.map +1 -0
  40. package/dist/core/__tests__/journal.test.js +65 -0
  41. package/dist/core/__tests__/journal.test.js.map +1 -1
  42. package/dist/core/__tests__/prompt.test.d.ts +2 -0
  43. package/dist/core/__tests__/prompt.test.d.ts.map +1 -0
  44. package/dist/core/__tests__/prompt.test.js +56 -0
  45. package/dist/core/__tests__/prompt.test.js.map +1 -0
  46. package/dist/core/fsx.d.ts +7 -1
  47. package/dist/core/fsx.d.ts.map +1 -1
  48. package/dist/core/fsx.js +18 -3
  49. package/dist/core/fsx.js.map +1 -1
  50. package/dist/core/hash.d.ts +13 -0
  51. package/dist/core/hash.d.ts.map +1 -0
  52. package/dist/core/hash.js +69 -0
  53. package/dist/core/hash.js.map +1 -0
  54. package/dist/core/journal.d.ts +10 -1
  55. package/dist/core/journal.d.ts.map +1 -1
  56. package/dist/core/journal.js +23 -1
  57. package/dist/core/journal.js.map +1 -1
  58. package/dist/core/prompt.d.ts +11 -0
  59. package/dist/core/prompt.d.ts.map +1 -0
  60. package/dist/core/prompt.js +34 -0
  61. package/dist/core/prompt.js.map +1 -0
  62. package/dist/index.js +5 -1
  63. package/dist/index.js.map +1 -1
  64. package/package.json +3 -1
  65. package/src/commands/add.ts +234 -16
  66. package/src/commands/checklist.ts +71 -0
  67. package/src/commands/remove.ts +105 -3
  68. package/src/commands/status.ts +47 -0
  69. package/src/core/__tests__/fsx.test.ts +101 -0
  70. package/src/core/__tests__/hash.test.ts +112 -0
  71. package/src/core/__tests__/journal.test.ts +76 -0
  72. package/src/core/__tests__/prompt.test.ts +72 -0
  73. package/src/core/fsx.ts +38 -5
  74. package/src/core/hash.ts +84 -0
  75. package/src/core/journal.ts +40 -2
  76. package/src/core/prompt.ts +40 -0
  77. package/src/index.ts +5 -1
  78. package/text.md +27 -0
@@ -1,11 +1,33 @@
1
1
  import { readFileContent, writeFileContent, exists, ensureDir } from './fsx.js';
2
2
  import { dirname } from 'path';
3
+ import { hashFile } from './hash.js';
3
4
  export async function readJournal(journalPath) {
4
5
  if (!(await exists(journalPath))) {
5
6
  return { entries: [] };
6
7
  }
7
8
  const content = await readFileContent(journalPath);
8
- return JSON.parse(content);
9
+ const journal = JSON.parse(content);
10
+ // Migrate old format to new format
11
+ let needsMigration = false;
12
+ for (const entry of journal.entries) {
13
+ if (entry.files.length > 0 && typeof entry.files[0] === 'string') {
14
+ needsMigration = true;
15
+ // Old format: array of strings
16
+ const oldFiles = entry.files;
17
+ // Convert to new format with hashes
18
+ const newFiles = [];
19
+ for (const filePath of oldFiles) {
20
+ const hash = await hashFile(filePath);
21
+ newFiles.push({ path: filePath, hash });
22
+ }
23
+ entry.files = newFiles;
24
+ }
25
+ }
26
+ // Save migrated journal
27
+ if (needsMigration) {
28
+ await writeJournal(journalPath, journal);
29
+ }
30
+ return journal;
9
31
  }
10
32
  export async function writeJournal(journalPath, journal) {
11
33
  await ensureDir(dirname(journalPath));
@@ -1 +1 @@
1
- {"version":3,"file":"journal.js","sourceRoot":"","sources":["../../src/core/journal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAChF,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAgB/B,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,WAAmB;IACnD,IAAI,CAAC,CAAC,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC;QACjC,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IACzB,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC,WAAW,CAAC,CAAC;IACnD,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;AAC7B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,WAAmB,EAAE,OAAgB;IACtE,MAAM,SAAS,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC;IACtC,MAAM,gBAAgB,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AACzF,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,WAAmB,EAAE,KAAmB;IACrE,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,WAAW,CAAC,CAAC;IAC/C,gDAAgD;IAChD,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CACtC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,KAAK,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,CAAC,CACjE,CAAC;IACF,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC5B,MAAM,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,WAAmB,EACnB,OAAe,EACf,MAAwB;IAExB,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,WAAW,CAAC,CAAC;IAC/C,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,OAAO,IAAI,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC;IACtF,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IAExB,OAAO,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CACtC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,KAAK,OAAO,IAAI,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CACrD,CAAC;IACF,MAAM,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IACzC,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,WAAmB,EACnB,OAAe,EACf,MAAwB;IAExB,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,WAAW,CAAC,CAAC;IAC/C,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,OAAO,IAAI,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC;AACzF,CAAC"}
1
+ {"version":3,"file":"journal.js","sourceRoot":"","sources":["../../src/core/journal.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Prompt user for input
3
+ * Returns empty string in non-interactive environments (CI/CD)
4
+ */
5
+ export declare function promptUser(question: string): string;
6
+ /**
7
+ * Prompt user for yes/no confirmation
8
+ * Returns defaultValue in non-interactive environments
9
+ */
10
+ export declare function promptYesNo(question: string, defaultYes?: boolean): boolean;
11
+ //# sourceMappingURL=prompt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../src/core/prompt.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAiBnD;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,UAAQ,GAAG,OAAO,CAUzE"}
@@ -0,0 +1,34 @@
1
+ import readlineSync from 'readline-sync';
2
+ /**
3
+ * Prompt user for input
4
+ * Returns empty string in non-interactive environments (CI/CD)
5
+ */
6
+ export function promptUser(question) {
7
+ // Check if we're in a non-interactive environment
8
+ if (!process.stdin.isTTY) {
9
+ return '';
10
+ }
11
+ // Check for CI environment variables
12
+ const isCI = process.env.CI === 'true' ||
13
+ process.env.GITHUB_ACTIONS === 'true' ||
14
+ process.env.GITLAB_CI === 'true' ||
15
+ process.env.CIRCLECI === 'true';
16
+ if (isCI) {
17
+ return '';
18
+ }
19
+ return readlineSync.question(question);
20
+ }
21
+ /**
22
+ * Prompt user for yes/no confirmation
23
+ * Returns defaultValue in non-interactive environments
24
+ */
25
+ export function promptYesNo(question, defaultYes = false) {
26
+ const answer = promptUser(question);
27
+ // Empty answer (non-interactive or just pressed enter)
28
+ if (answer === '') {
29
+ return defaultYes;
30
+ }
31
+ const normalized = answer.toLowerCase().trim();
32
+ return normalized === 'y' || normalized === 'yes';
33
+ }
34
+ //# sourceMappingURL=prompt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.js","sourceRoot":"","sources":["../../src/core/prompt.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,eAAe,CAAC;AAEzC;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,QAAgB;IACzC,kDAAkD;IAClD,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;QACzB,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,qCAAqC;IACrC,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,EAAE,KAAK,MAAM;QACzB,OAAO,CAAC,GAAG,CAAC,cAAc,KAAK,MAAM;QACrC,OAAO,CAAC,GAAG,CAAC,SAAS,KAAK,MAAM;QAChC,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,MAAM,CAAC;IAE7C,IAAI,IAAI,EAAE,CAAC;QACT,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,OAAO,YAAY,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AACzC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,QAAgB,EAAE,UAAU,GAAG,KAAK;IAC9D,MAAM,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IAEpC,uDAAuD;IACvD,IAAI,MAAM,KAAK,EAAE,EAAE,CAAC;QAClB,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,MAAM,UAAU,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;IAC/C,OAAO,UAAU,KAAK,GAAG,IAAI,UAAU,KAAK,KAAK,CAAC;AACpD,CAAC"}
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { Command } from 'commander';
3
- import pkg from '../package.json' assert { type: 'json' };
3
+ import pkg from '../package.json' with { type: 'json' };
4
4
  import { addCommand } from './commands/add.js';
5
5
  import { removeCommand } from './commands/remove.js';
6
6
  import { listCommand } from './commands/list.js';
@@ -8,6 +8,8 @@ import { doctorCommand } from './commands/doctor.js';
8
8
  import { loginCommand } from './commands/login.js';
9
9
  import { devicesCommand } from './commands/devices.js';
10
10
  import { logoutCommand } from './commands/logout.js';
11
+ import { statusCommand } from './commands/status.js';
12
+ import { checklistCommand } from './commands/checklist.js';
11
13
  const program = new Command();
12
14
  program
13
15
  .name('vf')
@@ -20,5 +22,7 @@ program.addCommand(doctorCommand);
20
22
  program.addCommand(listCommand);
21
23
  program.addCommand(addCommand);
22
24
  program.addCommand(removeCommand);
25
+ program.addCommand(statusCommand);
26
+ program.addCommand(checklistCommand);
23
27
  program.parse();
24
28
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,GAAG,MAAM,iBAAiB,CAAC,SAAS,IAAI,EAAE,MAAM,EAAE,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,IAAI,CAAC;KACV,WAAW,CAAC,oDAAoD,CAAC;KACjE,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,OAAO,CAAC,CAAC;AAEnC,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;AACnC,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;AAC/B,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;AAElC,OAAO,CAAC,KAAK,EAAE,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,GAAG,MAAM,iBAAiB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAE3D,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,IAAI,CAAC;KACV,WAAW,CAAC,oDAAoD,CAAC;KACjE,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,OAAO,CAAC,CAAC;AAEnC,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;AACnC,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;AAC/B,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC;AAErC,OAAO,CAAC,KAAK,EAAE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibefast-cli",
3
- "version": "0.1.3",
3
+ "version": "0.2.0",
4
4
  "description": "CLI for installing VibeFast features into your monorepo",
5
5
  "type": "module",
6
6
  "bin": {
@@ -24,10 +24,12 @@
24
24
  "dependencies": {
25
25
  "commander": "^12.0.0",
26
26
  "picocolors": "^1.0.0",
27
+ "readline-sync": "^1.4.10",
27
28
  "yauzl": "^2.10.0"
28
29
  },
29
30
  "devDependencies": {
30
31
  "@types/node": "^20.11.0",
32
+ "@types/readline-sync": "^1.4.8",
31
33
  "@types/yauzl": "^2.10.3",
32
34
  "tsx": "^4.7.0",
33
35
  "typescript": "^5.3.3",
@@ -4,20 +4,42 @@ import { getPaths } from '../core/paths.js';
4
4
  import { validateSignature, validateTarget } from '../core/validate.js';
5
5
  import { getToken, getDeviceInfo } from '../core/auth.js';
6
6
  import { fetchRecipe, downloadZip } from '../core/http.js';
7
- import { addEntry, getEntry } from '../core/journal.js';
7
+ import { addEntry, getEntry, FileEntry } from '../core/journal.js';
8
8
  import { copyTree, readFileContent } from '../core/fsx.js';
9
9
  import { insertNavLinkNative, insertNavLinkWeb } from '../core/codemod.js';
10
10
  import { join, resolve } from 'path';
11
11
  import { ensureWithinBase } from '../core/pathGuard.js';
12
12
  import { extractZipSafe } from '../core/archive.js';
13
+ import { hashFiles } from '../core/hash.js';
14
+ import { promptYesNo } from '../core/prompt.js';
13
15
 
14
16
  interface RecipeManifest {
15
17
  name: string;
16
18
  version: string;
17
19
  description: string;
18
20
  copy: Array<{ from: string; to: string }>;
19
- nav: { href: string; label: string };
21
+ nav?: { href: string; label: string };
20
22
  target: 'native' | 'web';
23
+ dependencies?: {
24
+ expo?: string[];
25
+ npm?: string[];
26
+ };
27
+ env?: Array<{
28
+ key: string;
29
+ description: string;
30
+ example: string;
31
+ link?: string;
32
+ }>;
33
+ manualSteps?: Array<{
34
+ title: string;
35
+ description: string;
36
+ link?: string;
37
+ file?: string;
38
+ content?: string;
39
+ }>;
40
+ postInstall?: {
41
+ message?: string;
42
+ };
21
43
  }
22
44
 
23
45
  export const addCommand = new Command('add')
@@ -25,7 +47,8 @@ export const addCommand = new Command('add')
25
47
  .argument('<feature>', 'Feature name to install')
26
48
  .option('--target <target>', 'Target platform (native or web)', 'native')
27
49
  .option('--dry-run', 'Preview changes without applying')
28
- .option('--force', 'Overwrite existing files')
50
+ .option('--force', 'Overwrite existing files without asking')
51
+ .option('--yes', 'Answer yes to all prompts (for automation)')
29
52
  .action(async (feature: string, options) => {
30
53
  try {
31
54
  const paths = getPaths();
@@ -66,10 +89,76 @@ export const addCommand = new Command('add')
66
89
  });
67
90
 
68
91
  if (!response.ok || (!response.signedUrl && !response.zipData)) {
69
- log.error(`Failed to fetch recipe: ${response.error || 'Unknown error'}`);
70
- if (response.message) {
71
- log.plain(` ${response.message}`);
92
+ const error = response.error || 'Unknown error';
93
+ const message = response.message || '';
94
+
95
+ log.plain('');
96
+ log.error('Failed to fetch recipe');
97
+ log.plain('');
98
+
99
+ // User-friendly error messages
100
+ if (error.includes('Invalid') || error.includes('token') || error.includes('license')) {
101
+ log.plain('❌ Invalid or expired license key');
102
+ log.plain('');
103
+ log.info('Your license key may be:');
104
+ log.plain(' • Incorrect or mistyped');
105
+ log.plain(' • Expired');
106
+ log.plain(' • Revoked');
107
+ log.plain('');
108
+ log.info('To fix this:');
109
+ log.plain(' 1. Check your license key from your purchase receipt');
110
+ log.plain(' 2. Run: vf logout');
111
+ log.plain(' 3. Run: vf login --token YOUR_CORRECT_TOKEN');
112
+ log.plain('');
113
+ log.info('Need help? Contact support@vibefast.pro');
114
+ } else if (error.includes('Device limit') || error.includes('device') || message.includes('device')) {
115
+ log.plain('❌ Device limit reached');
116
+ log.plain('');
117
+ log.info('You have reached the maximum number of devices for your license');
118
+ log.plain('');
119
+ log.info('To fix this:');
120
+ log.plain(' 1. Run: vf devices');
121
+ log.plain(' 2. Deactivate an unused device: vf devices --deactivate <device-id>');
122
+ log.plain(' 3. Try again: vf add ' + feature);
123
+ log.plain('');
124
+ if (message) {
125
+ log.plain(`Details: ${message}`);
126
+ log.plain('');
127
+ }
128
+ } else if (error.includes('Network') || error.includes('connect')) {
129
+ log.plain('❌ Network error');
130
+ log.plain('');
131
+ log.info('Could not connect to VibeFast servers');
132
+ log.plain('');
133
+ log.info('Please check:');
134
+ log.plain(' • Your internet connection');
135
+ log.plain(' • Firewall settings');
136
+ log.plain(' • VPN configuration');
137
+ log.plain('');
138
+ if (message) {
139
+ log.plain(`Details: ${message}`);
140
+ log.plain('');
141
+ }
142
+ } else if (error.includes('not found') || error.includes('404')) {
143
+ log.plain('❌ Feature not found');
144
+ log.plain('');
145
+ log.info(`The feature "${feature}" does not exist or is not available for ${target}`);
146
+ log.plain('');
147
+ log.info('To see available features:');
148
+ log.plain(' vf list');
149
+ log.plain('');
150
+ } else {
151
+ // Generic error
152
+ log.plain(`❌ ${error}`);
153
+ if (message) {
154
+ log.plain('');
155
+ log.plain(`Details: ${message}`);
156
+ }
157
+ log.plain('');
158
+ log.info('If this problem persists, contact support@vibefast.pro');
72
159
  }
160
+
161
+ log.plain('');
73
162
  process.exit(1);
74
163
  }
75
164
 
@@ -96,8 +185,11 @@ export const addCommand = new Command('add')
96
185
 
97
186
  log.info(`Installing ${manifest.name} v${manifest.version}...`);
98
187
 
99
- // Copy files
188
+ // Copy files with interactive confirmation
100
189
  const copiedFiles: string[] = [];
190
+ const allConflicts: string[] = [];
191
+ const allSkipped: string[] = [];
192
+
101
193
  for (const copySpec of manifest.copy) {
102
194
  const srcPath = ensureWithinBase(
103
195
  extractRoot,
@@ -111,11 +203,35 @@ export const addCommand = new Command('add')
111
203
  );
112
204
 
113
205
  log.info(`Copying ${copySpec.from} → ${copySpec.to}`);
114
- const files = await copyTree(srcPath, destPath, {
206
+ const result = await copyTree(srcPath, destPath, {
115
207
  dryRun: options.dryRun,
116
208
  force: options.force,
209
+ interactive: !options.force && !options.dryRun && !options.yes,
117
210
  });
118
- copiedFiles.push(...files);
211
+
212
+ copiedFiles.push(...result.files);
213
+ allConflicts.push(...result.conflicts);
214
+ allSkipped.push(...result.skipped);
215
+ }
216
+
217
+ // Show conflict warnings in dry-run mode
218
+ if (options.dryRun && allConflicts.length > 0) {
219
+ log.plain('');
220
+ log.warn(`⚠ ${allConflicts.length} file(s) will be overwritten:`);
221
+ allConflicts.slice(0, 5).forEach(f => {
222
+ const relativePath = f.replace(paths.cwd + '/', '');
223
+ log.plain(` • ${relativePath}`);
224
+ });
225
+ if (allConflicts.length > 5) {
226
+ log.plain(` ... and ${allConflicts.length - 5} more`);
227
+ }
228
+ log.warn('⚠ Make sure you have committed your changes to Git!');
229
+ log.plain('');
230
+ }
231
+
232
+ // Show skipped files
233
+ if (allSkipped.length > 0) {
234
+ log.info(`ℹ Skipped ${allSkipped.length} file(s) (you chose not to overwrite)`);
119
235
  }
120
236
 
121
237
  // Add watermark if provided
@@ -149,29 +265,131 @@ export const addCommand = new Command('add')
149
265
  }
150
266
  }
151
267
 
152
- // Update journal
268
+ // Hash files and update journal
153
269
  if (!options.dryRun) {
270
+ log.info('Computing file hashes...');
271
+ const fileHashes = await hashFiles(copiedFiles, { showProgress: copiedFiles.length > 20 });
272
+
273
+ const fileEntries: FileEntry[] = Array.from(fileHashes.entries()).map(([path, hash]) => ({
274
+ path,
275
+ hash,
276
+ }));
277
+
154
278
  await addEntry(paths.journalFile, {
155
279
  feature: manifest.name,
156
280
  target: manifest.target,
157
- files: copiedFiles,
281
+ files: fileEntries,
158
282
  insertedNav: navInserted,
159
283
  navHref,
160
284
  navLabel,
161
285
  ts: Date.now(),
286
+ manifest: {
287
+ version: manifest.version,
288
+ manualSteps: manifest.manualSteps,
289
+ env: manifest.env,
290
+ },
162
291
  });
163
292
  }
164
293
 
165
294
  log.success(`${manifest.name} installed successfully!`);
166
295
  log.info(`Files added: ${copiedFiles.length}`);
167
296
 
297
+ // Show required packages
298
+ if (manifest.dependencies) {
299
+ log.plain('');
300
+ log.warn('⚠ This feature requires additional packages');
301
+ log.plain('');
302
+
303
+ if (manifest.target === 'native' && manifest.dependencies.expo) {
304
+ const packages = manifest.dependencies.expo;
305
+
306
+ log.info('📦 Required packages:');
307
+ packages.forEach(pkg => {
308
+ log.plain(` • ${pkg}`);
309
+ });
310
+ log.plain('');
311
+
312
+ log.info('Install with:');
313
+ log.plain(` npx expo install ${packages.join(' ')}`);
314
+ log.plain('');
315
+ log.info('💡 Expo will automatically pick compatible versions');
316
+
317
+ } else if (manifest.target === 'web' && manifest.dependencies.npm) {
318
+ const packages = manifest.dependencies.npm;
319
+
320
+ log.info('📦 Required packages:');
321
+ packages.forEach(pkg => {
322
+ log.plain(` • ${pkg}`);
323
+ });
324
+ log.plain('');
325
+
326
+ log.info('Install with:');
327
+ log.plain(` pnpm add ${packages.join(' ')}`);
328
+ log.plain(' OR');
329
+ log.plain(` yarn add ${packages.join(' ')}`);
330
+ log.plain(` OR`);
331
+ log.plain(` npm install ${packages.join(' ')}`);
332
+ }
333
+
334
+ log.plain('');
335
+ }
336
+
337
+ // Show manual steps
338
+ if (manifest.manualSteps && !options.dryRun) {
339
+ log.plain('');
340
+ log.warn('⚠ MANUAL STEPS REQUIRED:');
341
+ log.plain('');
342
+ log.plain('This feature requires some manual configuration:');
343
+ log.plain('');
344
+
345
+ manifest.manualSteps.forEach((step, index) => {
346
+ log.plain(`Step ${index + 1}: ${step.title}`);
347
+ log.plain(` ${step.description}`);
348
+ if (step.link) {
349
+ log.plain(` 🔗 ${step.link}`);
350
+ }
351
+ if (step.file) {
352
+ log.plain(` 📝 File: ${step.file}`);
353
+ }
354
+ if (step.content) {
355
+ log.plain(` Add: ${step.content}`);
356
+ }
357
+ log.plain('');
358
+ });
359
+
360
+ log.info(`💡 Run 'vf checklist ${manifest.name}' to see these steps again`);
361
+ log.plain('');
362
+ }
363
+
364
+ // Show environment variables
365
+ if (manifest.env && !options.dryRun) {
366
+ log.plain('');
367
+ log.warn('⚠ REQUIRED ENVIRONMENT VARIABLES:');
368
+ log.plain('');
369
+
370
+ manifest.env.forEach(envVar => {
371
+ log.plain(` ${envVar.key}`);
372
+ log.plain(` ${envVar.description}`);
373
+ log.plain(` Example: ${envVar.example}`);
374
+ if (envVar.link) {
375
+ log.plain(` Get it: ${envVar.link}`);
376
+ }
377
+ log.plain('');
378
+ });
379
+
380
+ log.info('Add these to your .env file');
381
+ log.plain('');
382
+ }
383
+
384
+ // Post-install message
385
+ if (manifest.postInstall?.message && !options.dryRun) {
386
+ log.plain('');
387
+ log.info(manifest.postInstall.message);
388
+ }
389
+
168
390
  if (options.dryRun) {
391
+ log.plain('');
169
392
  log.warn('This was a dry run. Run without --dry-run to apply changes.');
170
- } else {
171
- log.info('Next steps:');
172
- log.plain(` 1. Review the changes in your repo`);
173
- log.plain(` 2. Run your dev server to test`);
174
- log.plain(` 3. Navigate to the new feature`);
175
393
  }
176
394
  } catch (error: any) {
177
395
  log.error(`Installation failed: ${error.message}`);
@@ -0,0 +1,71 @@
1
+ import { Command } from 'commander';
2
+ import { log } from '../core/log.js';
3
+ import { getPaths } from '../core/paths.js';
4
+ import { getEntry } from '../core/journal.js';
5
+
6
+ export const checklistCommand = new Command('checklist')
7
+ .description('Show manual setup steps for an installed feature')
8
+ .argument('<feature>', 'Feature name')
9
+ .option('--target <target>', 'Target platform (native or web)', 'native')
10
+ .action(async (feature: string, options) => {
11
+ try {
12
+ const paths = getPaths();
13
+ const target = options.target as 'native' | 'web';
14
+
15
+ // Check if feature is installed
16
+ const entry = await getEntry(paths.journalFile, feature, target);
17
+ if (!entry) {
18
+ log.error(`${feature} is not installed for ${target}`);
19
+ log.info('Run "vf status" to see installed features');
20
+ process.exit(1);
21
+ }
22
+
23
+ // Check if feature has manual steps
24
+ if (!entry.manifest?.manualSteps || entry.manifest.manualSteps.length === 0) {
25
+ log.info(`${feature} has no manual setup steps`);
26
+ log.success('This feature is ready to use!');
27
+ return;
28
+ }
29
+
30
+ // Display manual steps
31
+ log.info(`Manual setup steps for ${feature}:`);
32
+ log.plain('');
33
+
34
+ entry.manifest.manualSteps.forEach((step: any, index: number) => {
35
+ log.plain(`Step ${index + 1}: ${step.title}`);
36
+ log.plain(` ${step.description}`);
37
+ if (step.link) {
38
+ log.plain(` 🔗 ${step.link}`);
39
+ }
40
+ if (step.file) {
41
+ log.plain(` 📝 File: ${step.file}`);
42
+ }
43
+ if (step.content) {
44
+ log.plain(` Add: ${step.content}`);
45
+ }
46
+ log.plain('');
47
+ });
48
+
49
+ // Display environment variables if any
50
+ if (entry.manifest.env && entry.manifest.env.length > 0) {
51
+ log.warn('⚠ REQUIRED ENVIRONMENT VARIABLES:');
52
+ log.plain('');
53
+
54
+ entry.manifest.env.forEach((envVar: any) => {
55
+ log.plain(` ${envVar.key}`);
56
+ log.plain(` ${envVar.description}`);
57
+ log.plain(` Example: ${envVar.example}`);
58
+ if (envVar.link) {
59
+ log.plain(` Get it: ${envVar.link}`);
60
+ }
61
+ log.plain('');
62
+ });
63
+
64
+ log.info('Add these to your .env file');
65
+ }
66
+
67
+ } catch (error: any) {
68
+ log.error(`Failed to show checklist: ${error.message}`);
69
+ process.exit(1);
70
+ }
71
+ });