arkgate 2.8.3 → 2.9.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.
Files changed (55) hide show
  1. package/CHANGELOG.md +80 -0
  2. package/README.md +2 -1
  3. package/bin/ark-check.mjs +21 -3
  4. package/bin/ark-layer-match.mjs +88 -5
  5. package/bin/ark-mcp.mjs +7 -70
  6. package/bin/ark-shared.mjs +88 -8
  7. package/bin/ark.mjs +134 -22
  8. package/bin/lib/agent-gates.mjs +37 -3
  9. package/bin/lib/architecture-scan.mjs +16 -4
  10. package/bin/lib/config-warnings.mjs +11 -2
  11. package/bin/lib/doctor-plan.mjs +20 -0
  12. package/bin/lib/field-install.mjs +334 -0
  13. package/bin/lib/import-resolve.mjs +133 -0
  14. package/bin/lib/presets.mjs +207 -11
  15. package/bin/lib/remediation.mjs +15 -0
  16. package/bin/lib/suggestions.mjs +8 -3
  17. package/dist/eslint/index.cjs +63 -5
  18. package/dist/eslint/index.cjs.map +1 -1
  19. package/dist/eslint/index.d.cts +33 -1
  20. package/dist/eslint/index.d.ts +33 -1
  21. package/dist/eslint/index.js +63 -5
  22. package/dist/eslint/index.js.map +1 -1
  23. package/dist/index.cjs +103 -14
  24. package/dist/index.cjs.map +1 -1
  25. package/dist/index.d.cts +21 -9
  26. package/dist/index.d.ts +21 -9
  27. package/dist/index.js +103 -14
  28. package/dist/index.js.map +1 -1
  29. package/dist/nestjs/index.cjs +78 -4
  30. package/dist/nestjs/index.cjs.map +1 -1
  31. package/dist/nestjs/index.d.cts +1 -1
  32. package/dist/nestjs/index.d.ts +1 -1
  33. package/dist/nestjs/index.js +78 -4
  34. package/dist/nestjs/index.js.map +1 -1
  35. package/dist/runtime/index.cjs +103 -14
  36. package/dist/runtime/index.cjs.map +1 -1
  37. package/dist/runtime/index.d.cts +1 -1
  38. package/dist/runtime/index.d.ts +1 -1
  39. package/dist/runtime/index.js +103 -14
  40. package/dist/runtime/index.js.map +1 -1
  41. package/dist/{types-CSJhEOk2.d.cts → types-D6Q8WHes.d.cts} +7 -0
  42. package/dist/{types-CSJhEOk2.d.ts → types-D6Q8WHes.d.ts} +7 -0
  43. package/docs/agent-guide.md +55 -4
  44. package/package.json +3 -1
  45. package/server.json +2 -2
  46. package/templates/architecture-playbook.json +65 -1
  47. package/templates/policy-packs/enthusiast-ddd-bounded-contexts.json +19 -0
  48. package/templates/policy-packs/enthusiast-ui-surface.json +18 -0
  49. package/templates/policy-packs/enthusiast-vertical-slice.json +18 -0
  50. package/templates/skills/ark-adopt.md +4 -0
  51. package/templates/skills/ark-architect.md +5 -1
  52. package/templates/skills/ark-autopilot.md +10 -1
  53. package/templates/skills/ark-fix.md +3 -0
  54. package/templates/skills/ark-place.md +7 -0
  55. package/templates/skills/ark-think.md +43 -0
@@ -80,7 +80,7 @@ __export(runtime_exports, {
80
80
  module.exports = __toCommonJS(runtime_exports);
81
81
 
82
82
  // src/version.ts
83
- var version = "2.8.3";
83
+ var version = "2.9.1";
84
84
 
85
85
  // src/kernel/intent/IntentRegistry.ts
86
86
  var IntentRegistry = class {
@@ -743,6 +743,82 @@ async function recordInterceptorError(interceptor, event, error, deps) {
743
743
  });
744
744
  }
745
745
 
746
+ // src/domain/layerMatch.ts
747
+ function normalizeGlobSeparators(pattern) {
748
+ let out = "";
749
+ for (let i = 0; i < pattern.length; i += 1) {
750
+ const c = pattern[i];
751
+ if (c === "\\" && i + 1 < pattern.length) {
752
+ const next = pattern[i + 1];
753
+ if ("*?{}[],".includes(next) || next === "\\") {
754
+ out += "\\" + next;
755
+ i += 1;
756
+ continue;
757
+ }
758
+ out += "/";
759
+ continue;
760
+ }
761
+ out += c;
762
+ }
763
+ return out;
764
+ }
765
+ function sliceIdForPath(relPath, sliceFolders) {
766
+ if (!sliceFolders?.length) return void 0;
767
+ const parts = String(relPath).split(/[/\\]/).filter(Boolean);
768
+ const folders = new Set(sliceFolders.map((s) => String(s).toLowerCase()));
769
+ for (let i = 0; i < parts.length - 1; i += 1) {
770
+ if (folders.has(parts[i].toLowerCase())) {
771
+ return `${parts[i]}/${parts[i + 1]}`;
772
+ }
773
+ }
774
+ return void 0;
775
+ }
776
+ function inferSliceFoldersFromPatterns(patterns) {
777
+ const out = /* @__PURE__ */ new Set();
778
+ for (const pattern of patterns ?? []) {
779
+ const glob = normalizeGlobSeparators(String(pattern));
780
+ const parts = glob.split("/").filter(Boolean);
781
+ for (let i = 0; i < parts.length; i += 1) {
782
+ const part = parts[i];
783
+ if ((part === "**" || part === "*") && i > 0) {
784
+ const prev = parts[i - 1];
785
+ if (prev && !prev.includes("*") && !prev.includes("{") && !prev.includes("}")) {
786
+ out.add(prev);
787
+ }
788
+ }
789
+ }
790
+ }
791
+ return [...out];
792
+ }
793
+ function resolveSliceFolders(rule, layerName, layers) {
794
+ if (Array.isArray(rule.sliceFolders) && rule.sliceFolders.length > 0) {
795
+ return rule.sliceFolders.filter((s) => typeof s === "string" && s.length > 0);
796
+ }
797
+ const layer = (layers ?? []).find((l) => l.name === layerName);
798
+ return inferSliceFoldersFromPatterns(layer?.patterns);
799
+ }
800
+ function findDeniedEdgeRule(rules, from, to, options) {
801
+ for (const rule of rules ?? []) {
802
+ if (rule.from !== from || rule.to !== to) continue;
803
+ if (rule.allowed !== false) continue;
804
+ if (rule.peerIsolation) {
805
+ const fromPath = options?.fromPath;
806
+ const toPath = options?.toPath;
807
+ if (!fromPath || !toPath) continue;
808
+ const folders = resolveSliceFolders(rule, from, options?.layers);
809
+ if (folders.length === 0) continue;
810
+ const fromSlice = sliceIdForPath(fromPath, folders);
811
+ const toSlice = sliceIdForPath(toPath, folders);
812
+ if (!fromSlice || !toSlice) continue;
813
+ if (fromSlice !== toSlice) return rule;
814
+ continue;
815
+ }
816
+ if (from === to) continue;
817
+ return rule;
818
+ }
819
+ return void 0;
820
+ }
821
+
746
822
  // src/kernel/event-bus/observedLayerFlow.ts
747
823
  async function assertObservedLayerFlowAllowed(event, deps) {
748
824
  if (deps.mode === "off" || !deps.architectureProfile) {
@@ -754,9 +830,7 @@ async function assertObservedLayerFlowAllowed(event, deps) {
754
830
  const fromLayer = profile.resolveLayer(source);
755
831
  const toLayer = profile.resolveLayer(event.intent);
756
832
  if (!fromLayer || !toLayer) return;
757
- const blocked = profile.rules.find(
758
- (rule) => !rule.allowed && rule.from === fromLayer && rule.to === toLayer
759
- );
833
+ const blocked = findDeniedEdgeRule(profile.rules, fromLayer, toLayer);
760
834
  if (!blocked) return;
761
835
  const severity = deps.mode;
762
836
  const message = blocked.message ?? `Observed layer violation: "${source}" (${fromLayer}) must not produce "${event.intent}" (${toLayer}).`;
@@ -2406,16 +2480,26 @@ function createAICodeGate(options = {}) {
2406
2480
  }
2407
2481
  }
2408
2482
  for (const specifier of extractModuleSpecifiers(source)) {
2409
- const targetLayer = options.resolveImportLayer?.(specifier.value, filePath);
2410
- if (targetLayer && contextLayer && targetLayer !== contextLayer) {
2411
- const blocked = options.architectureProfile?.rules.find(
2412
- (rule) => !rule.allowed && rule.from === contextLayer && rule.to === targetLayer
2483
+ const targetHit = options.resolveImportTarget?.(specifier.value, filePath) ?? (options.resolveImportLayer ? { layer: options.resolveImportLayer(specifier.value, filePath) } : void 0);
2484
+ const sourceHit = typeof filePath === "string" ? options.resolveImportTarget?.(filePath) ?? (options.resolveImportLayer ? { layer: contextLayer, relPath: void 0 } : void 0) : void 0;
2485
+ const targetLayer = targetHit?.layer;
2486
+ if (targetLayer && contextLayer) {
2487
+ const blocked = findDeniedEdgeRule(
2488
+ options.architectureProfile?.rules,
2489
+ contextLayer,
2490
+ targetLayer,
2491
+ {
2492
+ fromPath: sourceHit?.relPath,
2493
+ toPath: targetHit?.relPath,
2494
+ layers: options.architectureLayers
2495
+ }
2413
2496
  );
2414
2497
  if (blocked) {
2498
+ const peer = Boolean(blocked.peerIsolation);
2415
2499
  violations.push(
2416
2500
  violation(
2417
2501
  "LAYER_IMPORT_VIOLATION",
2418
- blocked.message ?? `Layer "${contextLayer}" must not import "${targetLayer}".`,
2502
+ blocked.message ?? (peer ? `Layer "${contextLayer}" must not import across slices into "${targetLayer}".` : `Layer "${contextLayer}" must not import "${targetLayer}".`),
2419
2503
  {
2420
2504
  line: lineOf(source, specifier.index),
2421
2505
  source: specifier.value,
@@ -2423,13 +2507,16 @@ function createAICodeGate(options = {}) {
2423
2507
  filePath,
2424
2508
  fromLayer: contextLayer,
2425
2509
  toLayer: targetLayer,
2426
- suggestion: "Depend on a port/interface owned by an inner layer instead, or move this code to a layer allowed to make this import.",
2427
- details: { importKind: specifier.kind }
2510
+ suggestion: peer ? "Extract shared code to a shared layer, or coordinate slices via events/ports \u2014 do not import across feature/context slices." : "Depend on a port/interface owned by an inner layer instead, or move this code to a layer allowed to make this import.",
2511
+ details: { importKind: specifier.kind, peerIsolation: peer }
2428
2512
  }
2429
2513
  )
2430
2514
  );
2515
+ continue;
2516
+ }
2517
+ if (targetLayer !== contextLayer) {
2518
+ continue;
2431
2519
  }
2432
- continue;
2433
2520
  }
2434
2521
  if (exemptFromInfraHeuristics) continue;
2435
2522
  if (!hasInfrastructureToken(specifier.value) && !isKnownInfrastructurePackage(specifier.value)) {
@@ -2498,8 +2585,10 @@ function createAICodeGate(options = {}) {
2498
2585
  if (!looksLikeIntentName(literal.value)) continue;
2499
2586
  const targetLayer = options.architectureProfile.resolveLayer(literal.value);
2500
2587
  if (!targetLayer) continue;
2501
- const blocked = options.architectureProfile.rules.find(
2502
- (rule) => !rule.allowed && rule.from === contextLayer && rule.to === targetLayer
2588
+ const blocked = findDeniedEdgeRule(
2589
+ options.architectureProfile.rules,
2590
+ contextLayer,
2591
+ targetLayer
2503
2592
  );
2504
2593
  if (blocked) {
2505
2594
  violations.push(