paqad-ai 1.2.0 → 1.2.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # paqad-ai
2
2
 
3
+ ## 1.2.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#65](https://github.com/Eliyce/paqad-ai/pull/65) [`f398b00`](https://github.com/Eliyce/paqad-ai/commit/f398b00ced1d676a5e35452a3da7d143431569a0) Thanks [@HLasani](https://github.com/HLasani)! - Fix `paqad-ai onboard` hanging on the RAG "No, skip" path at the end of the full interactive prompt chain.
8
+
9
+ The orchestrator previously interleaved the RAG inquirer prompt with file writes: `resolveRagSelection()` ran early, and `writeDetectionReport` / `writeFrameworkMetadata` / `writeOnboardingManifest` / `writeDecisionPauseContractDocument` / `compileRules` / `initializeModuleHealth` / `classifier-config.json` / `next-steps.md` all ran _after_ it. When inquirer left a stuck readline handle on Node's event loop — observed reliably with the full prompt chain on the No-skip branch — every post-RAG write was silently dropped. Users were left with an incomplete `.paqad/**` and no `ONBOARDING COMPLETE` banner.
10
+
11
+ Onboarding is now two-phase. Phase 1 writes every core `.paqad/**` artifact and `CLAUDE.md`-equivalents with no inquirer prompts in the path. The new `onPhase1Complete` callback fires only after the onboarding manifest is on disk, so the success banner prints before phase 2 begins. Phase 2 owns the RAG opt-in (prompt → optional index build → idempotent `writeProjectProfile` update). If phase 2 prompts, hangs, fails, or is interrupted, every phase 1 artifact is already durable on disk.
12
+
13
+ Adds three orchestrator invariant unit tests that pin the new contract (`onPhase1Complete` fires after all core artifacts exist; a thrown `RagService.configureAndBuild` does not drop core writes; a thrown `resolveRagSelection` does not drop core writes) and a PTY-driven E2E (gated on the system `expect(1)` binary, skipped on platforms without it) that drives the real built CLI through the full interactive Laravel prompt chain, picks "No, skip" on RAG, and asserts the complete `.paqad/**` artifact set on disk.
14
+
15
+ Closes [#62](https://github.com/Eliyce/paqad-ai/issues/62).
16
+
3
17
  ## 1.2.0
4
18
 
5
19
  ### Minor Changes
package/dist/cli/index.js CHANGED
@@ -22042,7 +22042,33 @@ function toProjectRulePath(source) {
22042
22042
  }
22043
22043
 
22044
22044
  // src/onboarding/orchestrator.ts
22045
+ var NEXT_STEPS_MD = [
22046
+ "## Required: Create Documentation Foundation",
22047
+ "",
22048
+ "Before starting feature work, prompt your AI agent with:",
22049
+ "",
22050
+ "```text",
22051
+ "create documentation",
22052
+ "```",
22053
+ "",
22054
+ "This generates:",
22055
+ "- `docs/instructions/**`",
22056
+ "- `docs/instructions/rules/module-map.yml`",
22057
+ "",
22058
+ "Review `docs/instructions/rules/module-map.yml` first. Confirm that module and feature names use business language, then prompt your AI agent with:",
22059
+ "",
22060
+ "```text",
22061
+ "create module documentation",
22062
+ "```",
22063
+ "",
22064
+ "That second prompt generates `docs/modules/**` from the reviewed module map."
22065
+ ].join("\n");
22045
22066
  var OnboardingOrchestrator = class {
22067
+ /**
22068
+ * Two-phase onboarding. Phase 1 generates and writes every core artifact deterministically,
22069
+ * with no inquirer prompts. Phase 2 is the optional RAG opt-in: it can prompt, fail, or hang
22070
+ * and the project is still fully onboarded. See issue #62 for the regression this protects.
22071
+ */
22046
22072
  async run(options) {
22047
22073
  const detector = new Detector();
22048
22074
  const detection = await detector.detect(options.projectRoot);
@@ -22060,8 +22086,7 @@ var OnboardingOrchestrator = class {
22060
22086
  options.profileOverrides,
22061
22087
  options.projectRoot
22062
22088
  );
22063
- const ragSelection = await resolveRagSelection(selections.domain, options.selections?.rag);
22064
- profile.intelligence = applyRagSelection(profile.intelligence, ragSelection);
22089
+ profile.intelligence = applyRagSelection(profile.intelligence, void 0);
22065
22090
  const validator = new SchemaValidator();
22066
22091
  const validation = validator.validate("project-profile", profile);
22067
22092
  const modules = await discoverModules(options.projectRoot);
@@ -22120,17 +22145,6 @@ var OnboardingOrchestrator = class {
22120
22145
  const onboardingWarnings = [];
22121
22146
  writeProjectProfile2(options.projectRoot, profile);
22122
22147
  writeGitignore(options.projectRoot);
22123
- if (ragSelection?.enabled && ragSelection.provider) {
22124
- try {
22125
- await enableRagDuringOnboarding(options.projectRoot, ragSelection);
22126
- } catch (error) {
22127
- profile.intelligence = applyRagSelection(profile.intelligence, { enabled: false });
22128
- writeProjectProfile2(options.projectRoot, profile);
22129
- onboardingWarnings.push(
22130
- `RAG setup failed during onboarding: ${error instanceof Error ? error.message : "unknown error"}. Onboarding completed with RAG disabled.`
22131
- );
22132
- }
22133
- }
22134
22148
  writeDetectionReport(options.projectRoot, detection);
22135
22149
  writeFrameworkMetadata(options.projectRoot, VERSION);
22136
22150
  bootstrapFramework(options.projectRoot);
@@ -22213,6 +22227,15 @@ var OnboardingOrchestrator = class {
22213
22227
  `Classifier config initialization failed during onboarding: ${error instanceof Error ? error.message : "unknown error"}.`
22214
22228
  );
22215
22229
  }
22230
+ try {
22231
+ const nextStepsPath = join76(options.projectRoot, ".paqad", "next-steps.md");
22232
+ writeFileSync10(nextStepsPath, NEXT_STEPS_MD);
22233
+ writeResult.written.push(".paqad/next-steps.md");
22234
+ } catch (error) {
22235
+ onboardingWarnings.push(
22236
+ `Next-steps doc write failed: ${error instanceof Error ? error.message : "unknown error"}.`
22237
+ );
22238
+ }
22216
22239
  const manifestPath = writeOnboardingManifest(options.projectRoot, {
22217
22240
  framework_version: VERSION,
22218
22241
  adapter: adapters[0],
@@ -22232,7 +22255,7 @@ var OnboardingOrchestrator = class {
22232
22255
  classifier_config_path: ".paqad/classifier-config.json"
22233
22256
  }
22234
22257
  });
22235
- return {
22258
+ const phase1Output = {
22236
22259
  adapter: adapters[0],
22237
22260
  decision_pause_supported_adapters: adapters,
22238
22261
  generated_files: writeResult.written.map(toPosixPath),
@@ -22241,6 +22264,27 @@ var OnboardingOrchestrator = class {
22241
22264
  manifest_path: toPosixPath(manifestPath),
22242
22265
  warnings: [...writeResult.skipped, ...drift.review_targets, ...onboardingWarnings]
22243
22266
  };
22267
+ options.onPhase1Complete?.(phase1Output);
22268
+ const ragSelection = await resolveRagSelection(selections.domain, options.selections?.rag);
22269
+ if (ragSelection) {
22270
+ profile.intelligence = applyRagSelection(profile.intelligence, ragSelection);
22271
+ writeProjectProfile2(options.projectRoot, profile);
22272
+ }
22273
+ if (ragSelection?.enabled && ragSelection.provider) {
22274
+ try {
22275
+ await enableRagDuringOnboarding(options.projectRoot, ragSelection);
22276
+ } catch (error) {
22277
+ profile.intelligence = applyRagSelection(profile.intelligence, { enabled: false });
22278
+ writeProjectProfile2(options.projectRoot, profile);
22279
+ onboardingWarnings.push(
22280
+ `RAG setup failed during onboarding: ${error instanceof Error ? error.message : "unknown error"}. Onboarding completed with RAG disabled.`
22281
+ );
22282
+ }
22283
+ }
22284
+ return {
22285
+ ...phase1Output,
22286
+ warnings: [...writeResult.skipped, ...drift.review_targets, ...onboardingWarnings]
22287
+ };
22244
22288
  }
22245
22289
  };
22246
22290
  function buildProjectProfile(selections, snapshot, overrides, projectRoot) {
@@ -27935,7 +27979,7 @@ init_esm_shims();
27935
27979
  init_esm_shims();
27936
27980
 
27937
27981
  // src/index.ts
27938
- var VERSION = "1.2.0";
27982
+ var VERSION = "1.2.1";
27939
27983
 
27940
27984
  // src/cli/commands/capabilities.ts
27941
27985
  init_esm_shims();
@@ -29678,8 +29722,6 @@ function parseOptionalInteger(value) {
29678
29722
 
29679
29723
  // src/cli/commands/onboard.ts
29680
29724
  init_esm_shims();
29681
- import { mkdirSync as mkdirSync14, writeFileSync as writeFileSync12 } from "fs";
29682
- import { join as join113 } from "path";
29683
29725
  import { Command as Command7 } from "commander";
29684
29726
 
29685
29727
  // src/cli/ui/banner.ts
@@ -29754,42 +29796,17 @@ function createOnboardCommand() {
29754
29796
  stack: options.stack,
29755
29797
  capabilities: options.capability,
29756
29798
  providers: options.providers
29757
- } : void 0
29799
+ } : void 0,
29800
+ // Print the success banner as soon as the project is fully written to disk.
29801
+ // The optional RAG phase runs after this and cannot drop core onboarding state
29802
+ // even if it prompts, hangs, or fails. See #62.
29803
+ onPhase1Complete: () => {
29804
+ printNextSteps();
29805
+ }
29758
29806
  });
29759
- printNextSteps();
29760
- writeNextStepsFile(options.projectRoot);
29761
29807
  }
29762
29808
  );
29763
29809
  }
29764
- function writeNextStepsFile(projectRoot) {
29765
- const paqadDir = join113(projectRoot, ".paqad");
29766
- mkdirSync14(paqadDir, { recursive: true });
29767
- writeFileSync12(
29768
- join113(paqadDir, "next-steps.md"),
29769
- [
29770
- "## Required: Create Documentation Foundation",
29771
- "",
29772
- "Before starting feature work, prompt your AI agent with:",
29773
- "",
29774
- "```text",
29775
- "create documentation",
29776
- "```",
29777
- "",
29778
- "This generates:",
29779
- "- `docs/instructions/**`",
29780
- "- `docs/instructions/rules/module-map.yml`",
29781
- "",
29782
- "Review `docs/instructions/rules/module-map.yml` first. Confirm that module and feature names use business language, then prompt your AI agent with:",
29783
- "",
29784
- "```text",
29785
- "create module documentation",
29786
- "```",
29787
- "",
29788
- "That second prompt generates `docs/modules/**` from the reviewed module map."
29789
- ].join("\n"),
29790
- "utf8"
29791
- );
29792
- }
29793
29810
 
29794
29811
  // src/cli/commands/plan.ts
29795
29812
  init_esm_shims();
@@ -30384,8 +30401,8 @@ function createRagCommand() {
30384
30401
  // src/cli/commands/refresh.ts
30385
30402
  init_esm_shims();
30386
30403
  import { Command as Command12 } from "commander";
30387
- import { existsSync as existsSync57, readFileSync as readFileSync31, writeFileSync as writeFileSync13 } from "fs";
30388
- import { join as join114 } from "path";
30404
+ import { existsSync as existsSync57, readFileSync as readFileSync31, writeFileSync as writeFileSync12 } from "fs";
30405
+ import { join as join113 } from "path";
30389
30406
  init_paths();
30390
30407
  init_domain();
30391
30408
  init_project_profile();
@@ -30462,7 +30479,7 @@ async function refreshProviderEntries(projectRoot) {
30462
30479
  writeDecisionPauseContractDocument(projectRoot);
30463
30480
  for (const type of ADAPTER_TYPES) {
30464
30481
  const adapter = AdapterFactory.create(type);
30465
- const configPath = join114(projectRoot, adapter.getConfigPath());
30482
+ const configPath = join113(projectRoot, adapter.getConfigPath());
30466
30483
  if (!existsSync57(configPath)) {
30467
30484
  continue;
30468
30485
  }
@@ -30478,9 +30495,9 @@ function resolveRefreshStack(value) {
30478
30495
  return value !== void 0 && STACKS.includes(value) ? value : null;
30479
30496
  }
30480
30497
  function writeRefreshDrift(projectRoot, refreshDrift) {
30481
- const path11 = join114(projectRoot, PATHS.STACK_DRIFT);
30498
+ const path11 = join113(projectRoot, PATHS.STACK_DRIFT);
30482
30499
  const current = readExistingJson(path11);
30483
- writeFileSync13(path11, `${JSON.stringify({ ...current ?? {}, ...refreshDrift }, null, 2)}
30500
+ writeFileSync12(path11, `${JSON.stringify({ ...current ?? {}, ...refreshDrift }, null, 2)}
30484
30501
  `);
30485
30502
  }
30486
30503
  function readExistingJson(path11) {
@@ -30494,7 +30511,7 @@ function resolveChangedFiles(projectRoot, contextChangedFiles) {
30494
30511
  if (contextChangedFiles.length > 0) {
30495
30512
  return [...new Set(contextChangedFiles)].sort();
30496
30513
  }
30497
- const trackedPath = join114(projectRoot, PATHS.CHANGED_FILES);
30514
+ const trackedPath = join113(projectRoot, PATHS.CHANGED_FILES);
30498
30515
  if (!existsSync57(trackedPath)) {
30499
30516
  return [];
30500
30517
  }