@vyuhlabs/dxkit 2.21.2 → 2.22.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 (50) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/dist/analyzers/flow/config.d.ts +10 -0
  3. package/dist/analyzers/flow/config.d.ts.map +1 -1
  4. package/dist/analyzers/flow/config.js +29 -0
  5. package/dist/analyzers/flow/config.js.map +1 -1
  6. package/dist/analyzers/flow/contract.d.ts +12 -0
  7. package/dist/analyzers/flow/contract.d.ts.map +1 -1
  8. package/dist/analyzers/flow/contract.js +20 -0
  9. package/dist/analyzers/flow/contract.js.map +1 -1
  10. package/dist/analyzers/flow/diagnose.d.ts +62 -0
  11. package/dist/analyzers/flow/diagnose.d.ts.map +1 -0
  12. package/dist/analyzers/flow/diagnose.js +120 -0
  13. package/dist/analyzers/flow/diagnose.js.map +1 -0
  14. package/dist/analyzers/flow/publish.d.ts +47 -0
  15. package/dist/analyzers/flow/publish.d.ts.map +1 -0
  16. package/dist/analyzers/flow/publish.js +146 -0
  17. package/dist/analyzers/flow/publish.js.map +1 -0
  18. package/dist/analyzers/flow/setup.d.ts +71 -0
  19. package/dist/analyzers/flow/setup.d.ts.map +1 -0
  20. package/dist/analyzers/flow/setup.js +136 -0
  21. package/dist/analyzers/flow/setup.js.map +1 -0
  22. package/dist/cli.d.ts.map +1 -1
  23. package/dist/cli.js +52 -2
  24. package/dist/cli.js.map +1 -1
  25. package/dist/doctor.d.ts +7 -0
  26. package/dist/doctor.d.ts.map +1 -1
  27. package/dist/doctor.js +38 -1
  28. package/dist/doctor.js.map +1 -1
  29. package/dist/flow-cli.d.ts +10 -0
  30. package/dist/flow-cli.d.ts.map +1 -1
  31. package/dist/flow-cli.js +46 -0
  32. package/dist/flow-cli.js.map +1 -1
  33. package/dist/generator.d.ts.map +1 -1
  34. package/dist/generator.js +6 -0
  35. package/dist/generator.js.map +1 -1
  36. package/dist/prompts.d.ts +15 -0
  37. package/dist/prompts.d.ts.map +1 -1
  38. package/dist/prompts.js +66 -0
  39. package/dist/prompts.js.map +1 -1
  40. package/dist/workspace.d.ts +52 -0
  41. package/dist/workspace.d.ts.map +1 -0
  42. package/dist/workspace.js +130 -0
  43. package/dist/workspace.js.map +1 -0
  44. package/package.json +1 -1
  45. package/templates/.claude/skills/dxkit-config/SKILL.md +14 -0
  46. package/templates/.claude/skills/dxkit-fix/SKILL.md +2 -0
  47. package/templates/.claude/skills/dxkit-flow/SKILL.md +83 -0
  48. package/templates/.claude/skills/dxkit-hooks/SKILL.md +1 -1
  49. package/templates/.claude/skills/dxkit-init/SKILL.md +5 -0
  50. package/templates/.claude/skills/dxkit-onboard/SKILL.md +2 -0
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Flow setup — the detection + apply behind folding `flow init` into `init`.
3
+ *
4
+ * `init` runs `detectFlowTopology` to decide whether a repo even HAS a UI→API
5
+ * surface worth gating (if not, the init wizard stays silent — zero burden).
6
+ * When it does, init surfaces the two learnings this module computes — the
7
+ * dominant host-helper to strip, and any multiple backend services — as confirm
8
+ * prompts, then `applyFlowSetup` writes the resulting config (`.dxkit/policy.json:flow`
9
+ * plus `.dxkit/workspace.json` when participants are named).
10
+ *
11
+ * Detection reuses the same `gatherFlowModel` the gate runs (Rule 2 — one
12
+ * extractor); it never re-parses source itself. Fail-open throughout — an error
13
+ * degrades to "no flow detected", which the caller treats as "don't ask".
14
+ */
15
+ import { type FlowGateMode } from './config';
16
+ import { type WorkspaceParticipant } from '../../workspace';
17
+ import type { ClientCall, RouteEndpoint } from './extract';
18
+ /** Which halves of the UI→API contract this repo contains. */
19
+ export type FlowTopology = 'monorepo' | 'consumer-only' | 'provider-only' | 'none';
20
+ export interface FlowDetection {
21
+ /** monorepo (both sides) · consumer-only (calls, no routes) · provider-only
22
+ * (routes, no calls) · none (nothing to gate — stay silent). */
23
+ readonly topology: FlowTopology;
24
+ readonly callCount: number;
25
+ readonly routeCount: number;
26
+ /** Calls that already bind to a served route — the healthy baseline. */
27
+ readonly resolvedCount: number;
28
+ /** Dominant host-helper prefix(es) across client calls — the strip-prefix the
29
+ * setup offers so a call like `${Config.api()}/x` matches a served `/x`. */
30
+ readonly suggestedStripPrefixes: readonly string[];
31
+ /** Distinct top-level directories that serve routes — a best-effort
32
+ * multi-service hint (robust multi-repo splitting is M4.3's `flow publish`). */
33
+ readonly detectedServices: readonly string[];
34
+ }
35
+ /**
36
+ * Detect the repo's flow topology by running the shared extractor. Returns
37
+ * `topology: 'none'` (and the caller stays silent) when no flow-capable pack is
38
+ * active, when extraction finds nothing, or on any error.
39
+ */
40
+ export declare function detectFlowTopology(cwd: string): Promise<FlowDetection>;
41
+ /**
42
+ * The prefix of a raw client URL that is NOT part of the route path — an
43
+ * absolute `scheme://host`, or a leading `${...}` base-URL helper template. A
44
+ * relative URL (`/articles`) has no host prefix. This is exactly what a
45
+ * strip-prefix removes so a templated call matches a served route.
46
+ */
47
+ export declare function hostPrefixOf(rawUrl: string): string | null;
48
+ /** Host-helper prefixes ranked by frequency across calls, most common first.
49
+ * Empty when calls are all relative (nothing to strip). */
50
+ export declare function dominantHostPrefixes(calls: readonly ClientCall[]): string[];
51
+ /** Distinct top-level directories that contain served routes — a coarse
52
+ * multi-service signal. Returns [] when routes live under one top-level dir
53
+ * (a single service). Deeper (nested / multi-repo) service splitting is
54
+ * M4.3's concern; here we only surface the obvious top-level split. */
55
+ export declare function servicesFromRoutes(routes: readonly RouteEndpoint[]): string[];
56
+ /** The confirmed setup a caller applies after the init prompts. */
57
+ export interface FlowSetupDecision {
58
+ readonly mode: FlowGateMode;
59
+ readonly stripUrlPrefixes: readonly string[];
60
+ /** Named participants → written to workspace.json (the multi-service /
61
+ * cross-repo case). Omitted for a single-service monorepo. */
62
+ readonly participants?: readonly WorkspaceParticipant[];
63
+ }
64
+ /**
65
+ * Apply a confirmed flow setup: write `flow.mode` (+ strip prefixes) into
66
+ * `.dxkit/policy.json` via the canonical writer, and — when participants are
67
+ * named — `.dxkit/workspace.json`. Returns the repo-relative paths written (for
68
+ * the init summary). Never throws on an already-current policy (idempotent).
69
+ */
70
+ export declare function applyFlowSetup(cwd: string, decision: FlowSetupDecision): string[];
71
+ //# sourceMappingURL=setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../../src/analyzers/flow/setup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH,OAAO,EAAmB,KAAK,YAAY,EAAE,MAAM,UAAU,CAAC;AAC9D,OAAO,EAAkB,KAAK,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAC5E,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE3D,8DAA8D;AAC9D,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,eAAe,GAAG,eAAe,GAAG,MAAM,CAAC;AAEnF,MAAM,WAAW,aAAa;IAC5B;qEACiE;IACjE,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,wEAAwE;IACxE,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B;iFAC6E;IAC7E,QAAQ,CAAC,sBAAsB,EAAE,SAAS,MAAM,EAAE,CAAC;IACnD;qFACiF;IACjF,QAAQ,CAAC,gBAAgB,EAAE,SAAS,MAAM,EAAE,CAAC;CAC9C;AAWD;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAmC5E;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAU1D;AAED;4DAC4D;AAC5D,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,GAAG,MAAM,EAAE,CAO3E;AAED;;;wEAGwE;AACxE,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,aAAa,EAAE,GAAG,MAAM,EAAE,CAO7E;AAED,mEAAmE;AACnE,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,gBAAgB,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7C;mEAC+D;IAC/D,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,oBAAoB,EAAE,CAAC;CACzD;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,iBAAiB,GAAG,MAAM,EAAE,CAcjF"}
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+ /**
3
+ * Flow setup — the detection + apply behind folding `flow init` into `init`.
4
+ *
5
+ * `init` runs `detectFlowTopology` to decide whether a repo even HAS a UI→API
6
+ * surface worth gating (if not, the init wizard stays silent — zero burden).
7
+ * When it does, init surfaces the two learnings this module computes — the
8
+ * dominant host-helper to strip, and any multiple backend services — as confirm
9
+ * prompts, then `applyFlowSetup` writes the resulting config (`.dxkit/policy.json:flow`
10
+ * plus `.dxkit/workspace.json` when participants are named).
11
+ *
12
+ * Detection reuses the same `gatherFlowModel` the gate runs (Rule 2 — one
13
+ * extractor); it never re-parses source itself. Fail-open throughout — an error
14
+ * degrades to "no flow detected", which the caller treats as "don't ask".
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.detectFlowTopology = detectFlowTopology;
18
+ exports.hostPrefixOf = hostPrefixOf;
19
+ exports.dominantHostPrefixes = dominantHostPrefixes;
20
+ exports.servicesFromRoutes = servicesFromRoutes;
21
+ exports.applyFlowSetup = applyFlowSetup;
22
+ const languages_1 = require("../../languages");
23
+ const gather_1 = require("./gather");
24
+ const model_1 = require("./model");
25
+ const config_1 = require("./config");
26
+ const workspace_1 = require("../../workspace");
27
+ const NONE = {
28
+ topology: 'none',
29
+ callCount: 0,
30
+ routeCount: 0,
31
+ resolvedCount: 0,
32
+ suggestedStripPrefixes: [],
33
+ detectedServices: [],
34
+ };
35
+ /**
36
+ * Detect the repo's flow topology by running the shared extractor. Returns
37
+ * `topology: 'none'` (and the caller stays silent) when no flow-capable pack is
38
+ * active, when extraction finds nothing, or on any error.
39
+ */
40
+ async function detectFlowTopology(cwd) {
41
+ // Cheap gate first: if no active pack extracts flow, don't even run the
42
+ // extractor (keeps `init --yes` on a non-flow repo fast).
43
+ if ((0, languages_1.allFlowSourceExtensions)((0, languages_1.detectActiveLanguages)(cwd)).length === 0)
44
+ return NONE;
45
+ let model;
46
+ try {
47
+ model = await (0, gather_1.gatherFlowModel)({ roots: [cwd] });
48
+ }
49
+ catch {
50
+ return NONE;
51
+ }
52
+ const callCount = model.calls.length;
53
+ const routeCount = model.routes.length;
54
+ if (callCount === 0 && routeCount === 0)
55
+ return NONE;
56
+ const resolvedCount = model.bindings.filter((b) => b.route !== null && !(0, model_1.isPlaceholderOnlyPath)(b.route.path)).length;
57
+ const topology = callCount > 0 && routeCount > 0
58
+ ? 'monorepo'
59
+ : callCount > 0
60
+ ? 'consumer-only'
61
+ : 'provider-only';
62
+ return {
63
+ topology,
64
+ callCount,
65
+ routeCount,
66
+ resolvedCount,
67
+ suggestedStripPrefixes: dominantHostPrefixes(model.calls),
68
+ detectedServices: servicesFromRoutes(model.routes),
69
+ };
70
+ }
71
+ /**
72
+ * The prefix of a raw client URL that is NOT part of the route path — an
73
+ * absolute `scheme://host`, or a leading `${...}` base-URL helper template. A
74
+ * relative URL (`/articles`) has no host prefix. This is exactly what a
75
+ * strip-prefix removes so a templated call matches a served route.
76
+ */
77
+ function hostPrefixOf(rawUrl) {
78
+ // A template-literal URL is captured WITH its backticks (`${Config.api()}/x`);
79
+ // strip a leading one so the `${...}` head is at the string start. A plain
80
+ // string literal is captured without quotes, so this is a no-op for it.
81
+ const s = rawUrl.replace(/^`/, '');
82
+ const abs = /^(https?:\/\/[^/`]+)/.exec(s);
83
+ if (abs)
84
+ return abs[1];
85
+ const tmpl = /^(\$\{[^}]+\})/.exec(s);
86
+ if (tmpl)
87
+ return tmpl[1];
88
+ return null;
89
+ }
90
+ /** Host-helper prefixes ranked by frequency across calls, most common first.
91
+ * Empty when calls are all relative (nothing to strip). */
92
+ function dominantHostPrefixes(calls) {
93
+ const counts = new Map();
94
+ for (const c of calls) {
95
+ const p = hostPrefixOf(c.rawUrl);
96
+ if (p)
97
+ counts.set(p, (counts.get(p) ?? 0) + 1);
98
+ }
99
+ return [...counts.entries()].sort((a, b) => b[1] - a[1]).map(([p]) => p);
100
+ }
101
+ /** Distinct top-level directories that contain served routes — a coarse
102
+ * multi-service signal. Returns [] when routes live under one top-level dir
103
+ * (a single service). Deeper (nested / multi-repo) service splitting is
104
+ * M4.3's concern; here we only surface the obvious top-level split. */
105
+ function servicesFromRoutes(routes) {
106
+ const dirs = new Set();
107
+ for (const r of routes) {
108
+ const top = r.file.split(/[/\\]/)[0];
109
+ if (top && top !== r.file)
110
+ dirs.add(top); // skip files at repo root (no dir)
111
+ }
112
+ return dirs.size >= 2 ? [...dirs].sort() : [];
113
+ }
114
+ /**
115
+ * Apply a confirmed flow setup: write `flow.mode` (+ strip prefixes) into
116
+ * `.dxkit/policy.json` via the canonical writer, and — when participants are
117
+ * named — `.dxkit/workspace.json`. Returns the repo-relative paths written (for
118
+ * the init summary). Never throws on an already-current policy (idempotent).
119
+ */
120
+ function applyFlowSetup(cwd, decision) {
121
+ const written = [];
122
+ const changed = (0, config_1.writeFlowPolicy)(cwd, {
123
+ mode: decision.mode,
124
+ ...(decision.stripUrlPrefixes.length
125
+ ? { stripUrlPrefixes: [...decision.stripUrlPrefixes] }
126
+ : {}),
127
+ });
128
+ if (changed)
129
+ written.push('.dxkit/policy.json');
130
+ if (decision.participants && decision.participants.length > 0) {
131
+ (0, workspace_1.writeWorkspace)(cwd, { participants: [...decision.participants], external: [] });
132
+ written.push('.dxkit/workspace.json');
133
+ }
134
+ return written;
135
+ }
136
+ //# sourceMappingURL=setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.js","sourceRoot":"","sources":["../../../src/analyzers/flow/setup.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAuSA,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAq8DvD"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AA4SA,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAo/DvD"}
package/dist/cli.js CHANGED
@@ -39,6 +39,7 @@ const vendored_advisor_1 = require("./analyzers/tools/vendored-advisor");
39
39
  const detect_1 = require("./detect");
40
40
  const generator_1 = require("./generator");
41
41
  const prompts_1 = require("./prompts");
42
+ const setup_1 = require("./analyzers/flow/setup");
42
43
  const update_1 = require("./update");
43
44
  const doctor_1 = require("./doctor");
44
45
  const constants_1 = require("./constants");
@@ -247,6 +248,10 @@ function printUsage() {
247
248
  CLAUDE.md, preserving your content). Implies dxkit skills.
248
249
  --loop-preset <p> Loop blocking posture: security-only (default) or
249
250
  full-debt. Only meaningful with --claude-loop.
251
+ --flow Set up the UI→API integration gate (warn posture),
252
+ no prompt. Auto-offered interactively when init
253
+ detects a UI→API surface.
254
+ --no-flow Skip flow setup even when a UI→API surface is detected.
250
255
  --detect Auto-detect stack, minimal prompts
251
256
  --yes Accept all defaults, no prompts
252
257
  --force Overwrite existing files (incl. existing hooks/
@@ -349,6 +354,10 @@ async function run(argv) {
349
354
  // loop pack: register the Stop-gate hook + CLAUDE.md loop norm
350
355
  'claude-loop': { type: 'boolean', default: false },
351
356
  'loop-preset': { type: 'string' },
357
+ // flow setup (folded into init; no standalone `flow init`).
358
+ // --flow forces it on (warn posture); --no-flow suppresses it.
359
+ flow: { type: 'boolean', default: false },
360
+ 'no-flow': { type: 'boolean', default: false },
352
361
  // setup-branch-protection flags
353
362
  branch: { type: 'string' },
354
363
  'require-reviews': { type: 'string' },
@@ -465,9 +474,11 @@ async function run(argv) {
465
474
  // via flags but bundled under --full).
466
475
  // --claude-loop implies the dxkit skills so the dxkit-loop skill (and
467
476
  // its siblings) land — the loop is most useful when the user can ask
468
- // Claude to explain a block / switch presets conversationally.
477
+ // Claude to explain a block / switch presets conversationally. --flow
478
+ // does the same for the dxkit-flow skill: setting up the integration gate
479
+ // is most useful when Claude can then diagnose + repair a break with it.
469
480
  const wantClaudeLoop = !!values['claude-loop'];
470
- const wantDxkitAgents = !!values.full || !!values['with-dxkit-agents'] || wantClaudeLoop;
481
+ const wantDxkitAgents = !!values.full || !!values['with-dxkit-agents'] || wantClaudeLoop || !!values.flow;
471
482
  const result = await (0, generator_1.generate)(cwd, config, finalMode, !!values.force, !!values['no-scan'], wantDxkitAgents);
472
483
  // Phase Ship installers (additive). `--full` implies every flag
473
484
  // so a one-command setup gets the full 2.5.0 ship surface.
@@ -560,6 +571,37 @@ async function run(argv) {
560
571
  result: (0, ship_installers_1.installCiDeepSastRefresh)(cwd, { force: !!values.force }),
561
572
  });
562
573
  }
574
+ // Flow setup — folded into `init` (there is no standalone `flow init`).
575
+ // Detect a UI→API surface; if there is none, stay silent (zero burden on
576
+ // a library / CLI / data repo). Otherwise prompt for the gate posture
577
+ // (interactive, with a description of each) or take the gentle `warn`
578
+ // default (--flow / --yes / non-TTY). --no-flow suppresses it entirely.
579
+ if (!values['no-flow']) {
580
+ const detection = await (0, setup_1.detectFlowTopology)(cwd);
581
+ if (detection.topology !== 'none') {
582
+ // Non-TTY without an explicit answer can't prompt — fall to the
583
+ // default rather than hang (mirrors how --yes is handled).
584
+ const flowYes = promptOpts.yes || !process.stdin.isTTY;
585
+ const decision = await (0, prompts_1.promptFlowSetup)(detection, {
586
+ yes: flowYes,
587
+ forceOn: !!values.flow,
588
+ });
589
+ const written = (0, setup_1.applyFlowSetup)(cwd, decision);
590
+ shipResults.push({
591
+ label: 'Flow integration gate',
592
+ result: {
593
+ installed: written,
594
+ skipped: [],
595
+ sidecars: [],
596
+ notes: [
597
+ `Flow gate posture: ${decision.mode} ` +
598
+ `(${detection.callCount} call(s) → ${detection.routeCount} route(s)). ` +
599
+ `Change it in .dxkit/policy.json:flow.mode.`,
600
+ ],
601
+ },
602
+ });
603
+ }
604
+ }
563
605
  // dxkit must resolve project-locally so every installed self-invocation
564
606
  // surface (Stop hook, context-hook, pre-push + CI guardrail) can run a
565
607
  // pinned dxkit instead of 404-ing. The set of surfaces that imply this
@@ -865,12 +907,20 @@ async function run(argv) {
865
907
  await runFlowRefresh({ cwd, frontend, backend, specs, json: !!values.json });
866
908
  break;
867
909
  }
910
+ // `flow publish` → the multi-repo handshake: union every workspace
911
+ // participant's served routes into this repo's served.json.
912
+ if (subCommand === 'publish') {
913
+ const { runFlowPublish } = await Promise.resolve().then(() => __importStar(require('./flow-cli')));
914
+ await runFlowPublish({ cwd, frontend, backend, specs, json: !!values.json });
915
+ break;
916
+ }
868
917
  logger.fail(`Unknown flow subcommand: ${subCommand}`);
869
918
  logger.info('Usage:');
870
919
  logger.info(' vyuh-dxkit flow [map] [--frontend <dir>] [--backend <dir>] [--specs <a,b>]');
871
920
  logger.info(' vyuh-dxkit flow trace "<METHOD> <path>"');
872
921
  logger.info(' vyuh-dxkit flow extract [--out <dir>]');
873
922
  logger.info(' vyuh-dxkit flow refresh');
923
+ logger.info(' vyuh-dxkit flow publish (multi-repo: union participants’ served routes)');
874
924
  process.exit(1);
875
925
  break;
876
926
  }