sf-intelligence 0.1.0 → 0.1.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 (2) hide show
  1. package/dist/index.js +300 -55
  2. package/package.json +7 -7
package/dist/index.js CHANGED
@@ -944,7 +944,7 @@ var init_resolve_index = __esm({
944
944
  });
945
945
 
946
946
  // ../graph/dist/src/resolve.js
947
- var DEFAULT_LIMIT, MAX_LIMIT, MIN_BASE, MATCHED_FLOOR, NONE_THRESHOLD, EXACT_THRESHOLD, EXACT_COVERAGE, CONTENDER_RATIO, LENGTH_RATIO_FLOOR, SYNONYM_SCORE, POP_K, COVERAGE_EXP, STRONG_ANCHOR, TYPE_WEIGHT, typeWeight, scoreToken, rollupKind, buildEvidence, resolveComponents;
947
+ var DEFAULT_LIMIT, MAX_LIMIT, MIN_BASE, MATCHED_FLOOR, NONE_THRESHOLD, EXACT_THRESHOLD, EXACT_COVERAGE, CONTENDER_RATIO, LENGTH_RATIO_FLOOR, SYNONYM_SCORE, POP_K, COVERAGE_EXP, STRONG_ANCHOR, PARENT_MATCH_BONUS, TYPE_WEIGHT, typeWeight, scoreToken, rollupKind, buildEvidence, resolveComponents;
948
948
  var init_resolve = __esm({
949
949
  "../graph/dist/src/resolve.js"() {
950
950
  "use strict";
@@ -964,6 +964,7 @@ var init_resolve = __esm({
964
964
  POP_K = 0.08;
965
965
  COVERAGE_EXP = 0.5;
966
966
  STRONG_ANCHOR = 0.9;
967
+ PARENT_MATCH_BONUS = 1.1;
967
968
  TYPE_WEIGHT = {
968
969
  CustomObject: 1,
969
970
  CustomField: 0.95,
@@ -1053,12 +1054,34 @@ var init_resolve = __esm({
1053
1054
  qt,
1054
1055
  ...scoreToken(qt, node.tokens)
1055
1056
  }));
1057
+ const nameMatchedSomething = perToken.some((t) => t.score >= MATCHED_FLOOR);
1058
+ let parentMatched = false;
1059
+ if (nameMatchedSomething && node.parentApiName) {
1060
+ const parentTokens = tokenizeText(node.parentApiName);
1061
+ if (parentTokens.length > 0) {
1062
+ for (let i = 0; i < perToken.length; i += 1) {
1063
+ if (perToken[i].score >= MATCHED_FLOOR)
1064
+ continue;
1065
+ const pm = scoreToken(queryTokens[i], parentTokens);
1066
+ if (pm.score > perToken[i].score) {
1067
+ perToken[i] = {
1068
+ qt: queryTokens[i],
1069
+ score: pm.score,
1070
+ kind: "substring",
1071
+ matchedToken: pm.matchedToken
1072
+ };
1073
+ if (pm.score >= MATCHED_FLOOR)
1074
+ parentMatched = true;
1075
+ }
1076
+ }
1077
+ }
1078
+ }
1056
1079
  for (let i = 0; i < perToken.length; i += 1) {
1057
1080
  if (perToken[i].score > globalBest[i])
1058
1081
  globalBest[i] = perToken[i].score;
1059
1082
  }
1060
- const wholeExact = normQuery.length >= 2 && node.normName === normQuery;
1061
- pass1.push({ node, perToken, wholeExact });
1083
+ const wholeExact = normQuery.length >= 2 && node.normName === normQuery && query.includes(".") === node.apiName.includes(".");
1084
+ pass1.push({ node, perToken, wholeExact, parentMatched });
1062
1085
  }
1063
1086
  const anchorIdx = [];
1064
1087
  for (let i = 0; i < queryTokens.length; i += 1) {
@@ -1083,9 +1106,10 @@ var init_resolve = __esm({
1083
1106
  continue;
1084
1107
  const type = c.node.type;
1085
1108
  const refs = c.node.inbound;
1086
- const score = base * typeWeight(type) * (1 + POP_K * Math.log10(1 + refs));
1109
+ const score = base * typeWeight(type) * (1 + POP_K * Math.log10(1 + refs)) * (c.parentMatched ? PARENT_MATCH_BONUS : 1);
1087
1110
  const kind = c.wholeExact ? "exact" : rollupKind(matched);
1088
- const matchedNodeTokens = new Set(matched.map((m) => m.matchedToken));
1111
+ const nodeTokenSet = new Set(c.node.tokens);
1112
+ const matchedNodeTokens = new Set(matched.map((m) => m.matchedToken).filter((t) => nodeTokenSet.has(t)));
1089
1113
  coverageById.set(c.node.id, c.wholeExact ? 1 : matchedNodeTokens.size / c.node.tokens.length);
1090
1114
  if (c.wholeExact)
1091
1115
  wholeExactIds.add(c.node.id);
@@ -1549,6 +1573,7 @@ var init_manifest = __esm({
1549
1573
  const coveredTypes = filtered.filter((entry) => entry.requested && !entry.errored && !entry.neverModeled).map((entry) => entry.type);
1550
1574
  const partialTypes = filtered.filter((entry) => entry.errored && !entry.neverModeled).map((entry) => entry.type);
1551
1575
  const notModeledTypes = filtered.filter((entry) => entry.neverModeled).map((entry) => entry.type);
1576
+ const notRequestedTypes = filtered.filter((entry) => !entry.requested && !entry.neverModeled).map((entry) => entry.type);
1552
1577
  if (wanted !== null) {
1553
1578
  const knownTypes = new Set(filtered.map((entry) => entry.type));
1554
1579
  for (const type of wanted) {
@@ -1557,7 +1582,9 @@ var init_manifest = __esm({
1557
1582
  }
1558
1583
  }
1559
1584
  }
1560
- const missingCoverage = [.../* @__PURE__ */ new Set([...partialTypes, ...notModeledTypes])].sort();
1585
+ const missingCoverage = [
1586
+ .../* @__PURE__ */ new Set([...partialTypes, ...notModeledTypes, ...notRequestedTypes])
1587
+ ].sort();
1561
1588
  const coverageKnown = readCoverageEntries(manifest).length > 0;
1562
1589
  const status = missingCoverage.length > 0 ? "partial" : coverageKnown ? "complete" : "unknown";
1563
1590
  return {
@@ -7615,7 +7642,7 @@ var init_explain_field = __esm({
7615
7642
 
7616
7643
  // ../mcp/dist/src/tools/explain-flow.js
7617
7644
  import { z as z32 } from "zod";
7618
- var FLOW_PREFIX, DISCLOSURE2, explainFlowInputSchema, readFlowLabel, readFlowStatus, readFlowProcessType, readFlowTriggerType, findTriggerObject, collectTriggerConditions, stripObjectPrefix, collectActionCalls, prefixOf, collectRecordLookups, classifyWriteOperation, collectRecordWrites, decisionNameOf, collectDecisions, explainFlowHandler;
7645
+ var FLOW_PREFIX, DISCLOSURE2, explainFlowInputSchema, readFlowLabel, readFlowStatus, readFlowProcessType, readFlowTriggerType, findTriggerObject, collectTriggerConditions, stripObjectPrefix, collectActionCalls, prefixOf, collectRecordLookups, classifyWriteOperation, collectRecordWrites, decisionNameOf, collectDecisions, ALTERNATE_TYPE_PREFIXES, findAlternateTypeId, explainFlowHandler;
7619
7646
  var init_explain_flow = __esm({
7620
7647
  "../mcp/dist/src/tools/explain-flow.js"() {
7621
7648
  "use strict";
@@ -7788,6 +7815,21 @@ var init_explain_flow = __esm({
7788
7815
  }
7789
7816
  return out;
7790
7817
  };
7818
+ ALTERNATE_TYPE_PREFIXES = [
7819
+ "ApexTrigger",
7820
+ "ApexClass",
7821
+ "WorkflowRule",
7822
+ "ValidationRule"
7823
+ ];
7824
+ findAlternateTypeId = async (ctx, bareName) => {
7825
+ for (const prefix of ALTERNATE_TYPE_PREFIXES) {
7826
+ const candidate = `${prefix}:${bareName}`;
7827
+ const r = await getNodeById(ctx.graph, candidate);
7828
+ if (r.ok && r.value !== null)
7829
+ return candidate;
7830
+ }
7831
+ return null;
7832
+ };
7791
7833
  explainFlowHandler = async (ctx, input2) => {
7792
7834
  const coercedFlowId = coercePrefix(input2.flowId, [FLOW_PREFIX]);
7793
7835
  if (!coercedFlowId.startsWith(FLOW_PREFIX)) {
@@ -7806,9 +7848,11 @@ var init_explain_flow = __esm({
7806
7848
  });
7807
7849
  }
7808
7850
  if (nodeResult.value === null) {
7851
+ const bareName = flowId.slice(FLOW_PREFIX.length);
7852
+ const alt = await findAlternateTypeId(ctx, bareName);
7809
7853
  return err({
7810
7854
  kind: "component-not-found",
7811
- message: `no Flow with id ${flowId}`,
7855
+ message: alt ? `no Flow named '${bareName}', but '${alt}' exists \u2014 it is a ${alt.slice(0, alt.indexOf(":"))}, not a Flow. explain_flow only handles Flows; use get_component (or the matching trigger/apex tool) for '${alt}'.` : `no Flow with id ${flowId}`,
7812
7856
  path: flowId
7813
7857
  });
7814
7858
  }
@@ -9345,11 +9389,11 @@ var init_naming_convention = __esm({
9345
9389
  parseScope = (scope) => {
9346
9390
  if (scope === void 0 || scope === "all")
9347
9391
  return ok(null);
9348
- const match = /^CustomField:([^.]+)\.\*$/.exec(scope);
9392
+ const match = /^CustomField:([^.]+)(?:\.\*)?$/.exec(scope);
9349
9393
  if (match === null) {
9350
9394
  return err({
9351
9395
  kind: "invalid-scope",
9352
- message: `unrecognized scope "${scope}"; expected 'all' or 'CustomField:{ObjectApiName}.*'`
9396
+ message: `unrecognized scope "${scope}"; expected 'all', 'CustomField:{ObjectApiName}', or 'CustomField:{ObjectApiName}.*'`
9353
9397
  });
9354
9398
  }
9355
9399
  return ok(match[1]);
@@ -17053,7 +17097,7 @@ var init_get_edges = __esm({
17053
17097
  ];
17054
17098
  getEdgesInputSchema = z65.object({
17055
17099
  nodeId: z65.string().min(1),
17056
- direction: z65.enum(["in", "out", "both"]).optional(),
17100
+ direction: z65.preprocess((v) => v === "incoming" ? "in" : v === "outgoing" ? "out" : v, z65.enum(["in", "out", "both"])).optional(),
17057
17101
  edgeType: z65.enum(EDGE_TYPES).optional(),
17058
17102
  confidence: z65.enum(CONFIDENCE_LEVELS).optional()
17059
17103
  });
@@ -18920,7 +18964,7 @@ var init_live_consent = __esm({
18920
18964
  import { execFile as execFile2 } from "node:child_process";
18921
18965
  import { promisify as promisify2 } from "node:util";
18922
18966
  import { z as z73 } from "zod";
18923
- var nodeExecFile2, redactSecrets, LIVE_PLANE_DISCLOSURE, MAX_SAMPLE_ROWS, liveEnabledSchema, isLivePlaneEnabled, liveTrust, resolveOrg, resolveLiveAccess, liveConsentRequiredError, gateLive, getLiveAuth, runSfJson, apiPath, restGet, liveDescribeInputSchema, liveDescribeHandler, liveCountInputSchema, assertCountSoql, liveCountHandler, liveSampleInputSchema, capSampleSoql, liveSampleHandler, liveFieldPopulationInputSchema, liveFieldPopulationHandler, liveOrgLimitsInputSchema, liveOrgLimitsHandler, MAX_INACTIVE_USER_ROWS, DEFAULT_INACTIVE_DAYS, MS_PER_DAY, liveInactiveUsersInputSchema, soqlDateTime, liveInactiveUsersHandler, DEFAULT_LICENSE_INACTIVE_DAYS, MAX_RECLAIM_ROWS, LICENSE_USAGE_DISCLOSURE, liveLicenseUsageInputSchema, toUtilization, renderLicenseUsageMarkdown, liveLicenseUsageHandler, liveConsentInputSchema, consentTrust, liveConsentHandler, liveQuery, MAX_DETAIL_ROWS, daysAgoSoql, daysSince, livePlaneVaultState, UNAVAILABLE_ERROR, assertSoqlIdentifier, soqlLiteral, MAX_GROUP_BUCKETS, DEFAULT_STALE_DAYS, DEFAULT_RECENT_DAYS, liveGroupCountInputSchema, liveGroupCountHandler, liveStaleRecordsInputSchema, liveStaleRecordsHandler, liveRecentActivityInputSchema, liveRecentActivityHandler, buildEqualityWhere, aggregateCountFromRow, liveAggregateInputSchema, liveAggregateHandler, MAX_DUPLICATE_GROUPS, liveDuplicateCheckInputSchema, liveDuplicateCheckHandler, MAX_OWNER_BUCKETS, liveOwnerBreakdownInputSchema, liveOwnerBreakdownHandler, DEFAULT_REPORT_STALE_DAYS, liveReportUsageInputSchema, liveReportUsageHandler, liveFolderAccessInputSchema, liveFolderAccessHandler, DEFAULT_TEMPLATE_STALE_DAYS, CLASSIC_TEMPLATE_TYPES, liveEmailTemplateUsageInputSchema, liveEmailTemplateUsageHandler, DEFAULT_HEALTH_DAYS, LIMIT_RISK_THRESHOLD, liveOrgHealthInputSchema, liveOrgHealthHandler, liveStorageByObjectInputSchema, liveStorageByObjectHandler, liveDataSkewInputSchema, liveSetupAuditTrailInputSchema, liveSecurityExposureInputSchema;
18967
+ var nodeExecFile2, redactSecrets, LIVE_PLANE_DISCLOSURE, MAX_SAMPLE_ROWS, liveEnabledSchema, isLivePlaneEnabled, liveTrust, resolveOrg, resolveLiveAccess, liveConsentRequiredError, gateLive, getLiveAuth, runSfJson, apiPath, restGet, liveDescribeInputSchema, liveDescribeHandler, liveCountInputSchema, OBJECT_API_NAME_RE, resolveCountSoql, assertCountSoql, liveCountHandler, liveSampleInputSchema, capSampleSoql, liveSampleHandler, liveFieldPopulationInputSchema, liveFieldPopulationHandler, liveOrgLimitsInputSchema, liveOrgLimitsHandler, MAX_INACTIVE_USER_ROWS, DEFAULT_INACTIVE_DAYS, MS_PER_DAY, liveInactiveUsersInputSchema, soqlDateTime, liveInactiveUsersHandler, DEFAULT_LICENSE_INACTIVE_DAYS, MAX_RECLAIM_ROWS, LICENSE_USAGE_DISCLOSURE, liveLicenseUsageInputSchema, toUtilization, renderLicenseUsageMarkdown, liveLicenseUsageHandler, liveConsentInputSchema, consentTrust, liveConsentHandler, liveQuery, MAX_DETAIL_ROWS, daysAgoSoql, daysSince, livePlaneVaultState, UNAVAILABLE_ERROR, assertSoqlIdentifier, soqlLiteral, MAX_GROUP_BUCKETS, DEFAULT_STALE_DAYS, DEFAULT_RECENT_DAYS, liveGroupCountInputSchema, liveGroupCountHandler, liveStaleRecordsInputSchema, liveStaleRecordsHandler, liveRecentActivityInputSchema, liveRecentActivityHandler, buildEqualityWhere, aggregateCountFromRow, liveAggregateInputSchema, liveAggregateHandler, MAX_DUPLICATE_GROUPS, liveDuplicateCheckInputSchema, liveDuplicateCheckHandler, MAX_OWNER_BUCKETS, liveOwnerBreakdownInputSchema, liveOwnerBreakdownHandler, DEFAULT_REPORT_STALE_DAYS, liveReportUsageInputSchema, liveReportUsageHandler, liveFolderAccessInputSchema, liveFolderAccessHandler, DEFAULT_TEMPLATE_STALE_DAYS, CLASSIC_TEMPLATE_TYPES, liveEmailTemplateUsageInputSchema, liveEmailTemplateUsageHandler, DEFAULT_HEALTH_DAYS, LIMIT_RISK_THRESHOLD, liveOrgHealthInputSchema, liveOrgHealthHandler, liveStorageByObjectInputSchema, liveStorageByObjectHandler, liveDataSkewInputSchema, liveSetupAuditTrailInputSchema, liveSecurityExposureInputSchema;
18924
18968
  var init_live_plane = __esm({
18925
18969
  "../mcp/dist/src/tools/live-plane.js"() {
18926
18970
  "use strict";
@@ -19037,9 +19081,33 @@ var init_live_plane = __esm({
19037
19081
  });
19038
19082
  };
19039
19083
  liveCountInputSchema = liveEnabledSchema.extend({
19040
- soql: z73.string().min(1),
19084
+ // Either `soql` (a SELECT COUNT() query) OR `objectApiName` (count every row
19085
+ // of that object). Both optional at the schema level; the handler requires
19086
+ // exactly one and turns objectApiName into `SELECT COUNT() FROM <object>`.
19087
+ soql: z73.string().min(1).optional(),
19088
+ objectApiName: z73.string().min(1).optional(),
19041
19089
  orgAlias: z73.string().min(1).optional()
19042
19090
  });
19091
+ OBJECT_API_NAME_RE = /^[A-Za-z][A-Za-z0-9_]*$/;
19092
+ resolveCountSoql = (input2) => {
19093
+ if (input2.soql !== void 0)
19094
+ return ok(input2.soql);
19095
+ if (input2.objectApiName !== void 0) {
19096
+ if (!OBJECT_API_NAME_RE.test(input2.objectApiName)) {
19097
+ return err({
19098
+ kind: "invalid-query",
19099
+ message: `objectApiName "${input2.objectApiName}" is not a valid Salesforce object API name.`,
19100
+ path: "objectApiName"
19101
+ });
19102
+ }
19103
+ return ok(`SELECT COUNT() FROM ${input2.objectApiName}`);
19104
+ }
19105
+ return err({
19106
+ kind: "invalid-query",
19107
+ message: "live_count needs either `soql` (a SELECT COUNT() query) or `objectApiName`.",
19108
+ path: "soql"
19109
+ });
19110
+ };
19043
19111
  assertCountSoql = (soql) => {
19044
19112
  const normalized = soql.trim().replace(/\s+/g, " ");
19045
19113
  if (!/^select\s+count\s*\(/i.test(normalized)) {
@@ -19055,7 +19123,10 @@ var init_live_plane = __esm({
19055
19123
  const gate = await gateLive(ctx, input2);
19056
19124
  if (!gate.ok)
19057
19125
  return gate;
19058
- const soqlCheck = assertCountSoql(input2.soql);
19126
+ const soqlResult = resolveCountSoql(input2);
19127
+ if (!soqlResult.ok)
19128
+ return soqlResult;
19129
+ const soqlCheck = assertCountSoql(soqlResult.value);
19059
19130
  if (!soqlCheck.ok)
19060
19131
  return soqlCheck;
19061
19132
  const org = resolveOrg(ctx, input2.orgAlias);
@@ -21223,7 +21294,7 @@ var init_omniuicard_widget_breakdown = __esm({
21223
21294
  });
21224
21295
 
21225
21296
  // ../mcp/dist/src/tools/soe-admission.js
21226
- var OBJECT_NOT_MODELED_BOUNDARY, evaluateSoeAdmission, composeSoeDisclosure;
21297
+ var OBJECT_NOT_MODELED_BOUNDARY, evaluateSoeAdmission, composeSoeDisclosure, soeNotAdmittedMessage;
21227
21298
  var init_soe_admission = __esm({
21228
21299
  "../mcp/dist/src/tools/soe-admission.js"() {
21229
21300
  "use strict";
@@ -21259,9 +21330,74 @@ var init_soe_admission = __esm({
21259
21330
  if (parentOf.value.length > 0) {
21260
21331
  return ok({ admitted: true, objectModeled: false });
21261
21332
  }
21262
- return ok({ admitted: false, objectModeled: false });
21333
+ const inbound = await listEdges(ctx.graph, objectId, { direction: "in" });
21334
+ if (!inbound.ok) {
21335
+ return err(inbound.error.message);
21336
+ }
21337
+ return ok({
21338
+ admitted: false,
21339
+ objectModeled: false,
21340
+ referencedButNotModeled: inbound.value.length > 0
21341
+ });
21263
21342
  };
21264
21343
  composeSoeDisclosure = (baseDisclosure, objectModeled) => objectModeled ? baseDisclosure : `${baseDisclosure} ${OBJECT_NOT_MODELED_BOUNDARY}`;
21344
+ soeNotAdmittedMessage = (objectId, referencedButNotModeled) => referencedButNotModeled ? `\`${objectId}\` is referenced by this org (e.g. permission-set grants) but its definition was never retrieved into this vault \u2014 typically a managed-package or filtered object. Save-order can't be composed without the object's own metadata and automation. Run \`sfi refresh\` if it is retrievable, otherwise treat it as external.` : `no automation or object definition for \`${objectId}\` in this vault`;
21345
+ }
21346
+ });
21347
+
21348
+ // ../mcp/dist/src/tools/soe-payload-bounds.js
21349
+ var SOE_MAX_PAYLOAD_BYTES, KEEP_ALL_AT_OR_BELOW, sizeOf, enforceSoeByteBudget, soeTruncationNote;
21350
+ var init_soe_payload_bounds = __esm({
21351
+ "../mcp/dist/src/tools/soe-payload-bounds.js"() {
21352
+ "use strict";
21353
+ SOE_MAX_PAYLOAD_BYTES = 4e4;
21354
+ KEEP_ALL_AT_OR_BELOW = 4;
21355
+ sizeOf = (payload) => Buffer.byteLength(JSON.stringify(payload), "utf8");
21356
+ enforceSoeByteBudget = (payload, steps) => {
21357
+ if (sizeOf(payload) <= SOE_MAX_PAYLOAD_BYTES) {
21358
+ return { truncated: false, actionsOmitted: 0 };
21359
+ }
21360
+ let totalOmitted = 0;
21361
+ const trimTo = (step3, keep) => {
21362
+ if (keep >= step3.actions.length)
21363
+ return;
21364
+ const removed = step3.actions.length - keep;
21365
+ step3.actions = step3.actions.slice(0, keep);
21366
+ step3.actionsOmitted = (step3.actionsOmitted ?? 0) + removed;
21367
+ totalOmitted += removed;
21368
+ };
21369
+ const actionBytes = steps.reduce((n, s) => n + Buffer.byteLength(JSON.stringify(s.actions), "utf8"), 0);
21370
+ const nonActionBytes = Math.max(0, sizeOf(payload) - actionBytes);
21371
+ const budgetForActions = SOE_MAX_PAYLOAD_BYTES - nonActionBytes;
21372
+ if (budgetForActions <= 0) {
21373
+ for (const s of steps)
21374
+ trimTo(s, 0);
21375
+ } else if (actionBytes > budgetForActions) {
21376
+ const ratio = budgetForActions / actionBytes;
21377
+ for (const s of steps) {
21378
+ if (s.actions.length <= KEEP_ALL_AT_OR_BELOW)
21379
+ continue;
21380
+ trimTo(s, Math.max(0, Math.floor(s.actions.length * ratio)));
21381
+ }
21382
+ }
21383
+ for (let guard = 0; guard < 1e5; guard += 1) {
21384
+ if (sizeOf(payload) <= SOE_MAX_PAYLOAD_BYTES)
21385
+ break;
21386
+ let target;
21387
+ for (const s of steps) {
21388
+ if (s.actions.length <= KEEP_ALL_AT_OR_BELOW)
21389
+ continue;
21390
+ if (target === void 0 || s.actions.length > target.actions.length) {
21391
+ target = s;
21392
+ }
21393
+ }
21394
+ if (target === void 0)
21395
+ break;
21396
+ trimTo(target, Math.floor(target.actions.length / 2));
21397
+ }
21398
+ return { truncated: totalOmitted > 0, actionsOmitted: totalOmitted };
21399
+ };
21400
+ soeTruncationNote = (n) => `Response trimmed to fit the ~${Math.round(SOE_MAX_PAYLOAD_BYTES / 1e3)} KB MCP response budget: every save-order STEP is present and in order, but ${n} per-step action edge(s) across the heaviest steps were omitted (see each step's \`actionsOmitted\`). Query a single object/event or use \`get_edges\` on a specific step's component for its full action list.`;
21265
21401
  }
21266
21402
  });
21267
21403
 
@@ -21274,6 +21410,7 @@ var init_order_of_execution = __esm({
21274
21410
  init_dist();
21275
21411
  init_src();
21276
21412
  init_soe_admission();
21413
+ init_soe_payload_bounds();
21277
21414
  DISCLOSURE6 = "v2.0e composes the documented Salesforce order-of-execution instantiated against THIS org's extracted automation. Conditions ARE listed but NOT EVALUATED \u2014 the tool does not know whether this particular record satisfies them at runtime. Manual sharing, sharing sets, account teams, and Apex callouts after save are out of scope.";
21278
21415
  SOE_EVENTS = ["insert", "update", "delete", "undelete"];
21279
21416
  orderOfExecutionInputSchema = z82.object({
@@ -21603,7 +21740,7 @@ var init_order_of_execution = __esm({
21603
21740
  if (!admission.value.admitted) {
21604
21741
  return err({
21605
21742
  kind: "component-not-found",
21606
- message: `no automation or object definition for \`${objectId}\` in this vault`,
21743
+ message: soeNotAdmittedMessage(objectId, admission.value.referencedButNotModeled ?? false),
21607
21744
  path: objectId
21608
21745
  });
21609
21746
  }
@@ -21621,13 +21758,20 @@ var init_order_of_execution = __esm({
21621
21758
  }
21622
21759
  byEvent[event] = perEventResult.value;
21623
21760
  }
21761
+ const data = {
21762
+ objectApiName: input2.objectApiName,
21763
+ objectModeled,
21764
+ byEvent,
21765
+ disclosure: composeSoeDisclosure(DISCLOSURE6, objectModeled)
21766
+ };
21767
+ const allSteps = SOE_EVENTS.flatMap((event) => byEvent[event].soe);
21768
+ const budget = enforceSoeByteBudget(data, allSteps);
21769
+ if (budget.truncated) {
21770
+ data.truncated = true;
21771
+ data.disclosure = `${data.disclosure} ${soeTruncationNote(budget.actionsOmitted)}`;
21772
+ }
21624
21773
  return ok({
21625
- data: {
21626
- objectApiName: input2.objectApiName,
21627
- objectModeled,
21628
- byEvent,
21629
- disclosure: composeSoeDisclosure(DISCLOSURE6, objectModeled)
21630
- },
21774
+ data,
21631
21775
  vaultState: {
21632
21776
  sourceTreeHash: ctx.manifest.sourceTreeHash,
21633
21777
  refreshedAt: ctx.manifest.refreshedAt
@@ -22755,7 +22899,9 @@ var init_intent_router = __esm({
22755
22899
  /\bwho\s+can\s+(see|read|view|edit|access)\b\s+[\w\s.]+[?.!]?$/,
22756
22900
  /\b(access|permission|fls|field[-\s]level\s+security)\b.*\b(field|object)\b/,
22757
22901
  /\bwho\s+has\s+access\s+to\b/,
22758
- /\bwhich\s+(profiles?|permission\s+sets?)\b.*\b(grant|access|see|edit)\b/
22902
+ // "allow|read|view" added: "which permission sets allow read on X" was a
22903
+ // router gap (the verbs grant/access/see/edit didn't cover it).
22904
+ /\bwhich\s+(profiles?|permission\s+sets?)\b.*\b(grant|allow|access|see|read|view|edit)\b/
22759
22905
  ]
22760
22906
  },
22761
22907
  {
@@ -22880,11 +23026,15 @@ var init_intent_router = __esm({
22880
23026
  tools: ["sfi.resolve", "sfi.what_happens_on_save", "sfi.order_of_execution"],
22881
23027
  liveRequired: false,
22882
23028
  needsResolve: true,
22883
- reason: "Order of execution / what runs on save is reconstructed from the vault graph.",
23029
+ reason: "Order of execution / what runs on save is reconstructed from the vault graph. what_happens_on_save needs an explicit DML event \u2014 default to 'update' (or insert/delete to match the question) when none is stated.",
22884
23030
  patterns: [
22885
23031
  /\b(trigger\s+order|order\s+of\s+execution)\b/,
22886
23032
  /\bwhat\s+(happens|runs|fires)\b.*\b(on\s+save|when\b.*\b(created|saved|updated|inserted|deleted))\b/,
22887
- /\bwhat\s+(triggers?|automation|flows?)\b.*\b(fire|run)\b/
23033
+ // "which/what flows|triggers|VRs|workflows run|fire when ..." — the
23034
+ // "which" phrasing was a router gap (e.g. "which flows run when a Case is
23035
+ // created"), so the question fell through to unrouted.
23036
+ /\b(what|which)\s+(triggers?|automation|flows?|validation\s+rules?|workflows?)\b.*\b(fire|run|execute|happen)\b/,
23037
+ /\b(flows?|triggers?|automation)\b.*\bwhen\b.*\b\w+\s+is\b.*\b(created|updated|inserted|deleted|saved)\b/
22888
23038
  ]
22889
23039
  },
22890
23040
  {
@@ -26078,6 +26228,7 @@ var init_what_happens_on_save = __esm({
26078
26228
  init_dist();
26079
26229
  init_src();
26080
26230
  init_soe_admission();
26231
+ init_soe_payload_bounds();
26081
26232
  DISCLOSURE7 = "v2.0e composes the documented Salesforce order-of-execution instantiated against THIS org's extracted automation. Conditions ARE listed but NOT EVALUATED \u2014 the tool does not know whether this particular record satisfies them at runtime. Manual sharing, sharing sets, account teams, and Apex callouts after save are out of scope.";
26082
26233
  ALLOWED_EVENTS = [
26083
26234
  "insert",
@@ -26088,7 +26239,10 @@ var init_what_happens_on_save = __esm({
26088
26239
  ];
26089
26240
  whatHappensOnSaveInputSchema = z103.object({
26090
26241
  objectApiName: z103.string().min(1),
26091
- event: z103.enum(ALLOWED_EVENTS),
26242
+ // Accept "after update" / "Before Insert" etc.: lower-case and drop the
26243
+ // before/after timing prefix so the bare DML event matches the enum. The
26244
+ // SOE walker models both timings internally; the event arg selects the row.
26245
+ event: z103.preprocess((v) => typeof v === "string" ? v.trim().toLowerCase().replace(/^(?:before|after)\s+/, "") : v, z103.enum(ALLOWED_EVENTS)),
26092
26246
  recordTypeId: z103.string().min(1).optional()
26093
26247
  });
26094
26248
  workflowMatchesEvent2 = (triggerType, event) => {
@@ -26307,7 +26461,7 @@ var init_what_happens_on_save = __esm({
26307
26461
  if (!admission.value.admitted) {
26308
26462
  return err({
26309
26463
  kind: "component-not-found",
26310
- message: `no automation or object definition for \`${objectId}\` in this vault`,
26464
+ message: soeNotAdmittedMessage(objectId, admission.value.referencedButNotModeled ?? false),
26311
26465
  path: objectId
26312
26466
  });
26313
26467
  }
@@ -26444,20 +26598,26 @@ var init_what_happens_on_save = __esm({
26444
26598
  const asyncFanOut = asyncStepsResult.value.length;
26445
26599
  stepIndex += asyncFanOut;
26446
26600
  const conditionalCount = soe.filter((s) => s.conditional !== void 0).length;
26447
- return ok({
26448
- data: {
26449
- objectApiName: input2.objectApiName,
26450
- event: input2.event,
26451
- recordTypeId: input2.recordTypeId ?? null,
26452
- objectModeled,
26453
- soe,
26454
- summary: {
26455
- totalSteps: soe.length,
26456
- conditionalSteps: conditionalCount,
26457
- asyncFanOut
26458
- },
26459
- disclosure: composeSoeDisclosure(DISCLOSURE7, objectModeled)
26601
+ const data = {
26602
+ objectApiName: input2.objectApiName,
26603
+ event: input2.event,
26604
+ recordTypeId: input2.recordTypeId ?? null,
26605
+ objectModeled,
26606
+ soe,
26607
+ summary: {
26608
+ totalSteps: soe.length,
26609
+ conditionalSteps: conditionalCount,
26610
+ asyncFanOut
26460
26611
  },
26612
+ disclosure: composeSoeDisclosure(DISCLOSURE7, objectModeled)
26613
+ };
26614
+ const budget = enforceSoeByteBudget(data, soe);
26615
+ if (budget.truncated) {
26616
+ data.truncated = true;
26617
+ data.disclosure = `${data.disclosure} ${soeTruncationNote(budget.actionsOmitted)}`;
26618
+ }
26619
+ return ok({
26620
+ data,
26461
26621
  vaultState: {
26462
26622
  sourceTreeHash: ctx.manifest.sourceTreeHash,
26463
26623
  refreshedAt: ctx.manifest.refreshedAt
@@ -28731,7 +28891,8 @@ var init_tools = __esm({
28731
28891
  type: "object",
28732
28892
  properties: {
28733
28893
  nodeId: { type: "string", minLength: 1 },
28734
- direction: { type: "string", enum: ["in", "out", "both"] },
28894
+ // 'incoming'/'outgoing' are accepted aliases (normalized to in/out).
28895
+ direction: { type: "string", enum: ["in", "out", "both", "incoming", "outgoing"] },
28735
28896
  edgeType: {
28736
28897
  type: "string",
28737
28898
  // Single-sourced from the contracts EDGE_TYPES tuple so the advertised
@@ -28914,9 +29075,12 @@ var init_tools = __esm({
28914
29075
  });
28915
29076
  LIVE_COUNT_INPUT_SCHEMA = Object.freeze({
28916
29077
  type: "object",
28917
- required: ["soql"],
29078
+ // Either `soql` (a SELECT COUNT() query) or `objectApiName` (count all rows).
29079
+ // The one-of requirement is enforced in the handler, not the JSON schema, so
29080
+ // the advertised shape stays simple for clients.
28918
29081
  properties: {
28919
29082
  soql: { type: "string", minLength: 1 },
29083
+ objectApiName: { type: "string", minLength: 1 },
28920
29084
  ...LIVE_ENABLED_PROPERTY
28921
29085
  }
28922
29086
  });
@@ -30195,7 +30359,7 @@ var init_tools = __esm({
30195
30359
  },
30196
30360
  {
30197
30361
  name: "sfi.live_count",
30198
- description: "Opt-in live org: run a SELECT COUNT() SOQL query with strict shape validation. Read-only; never falls back to vault data.",
30362
+ description: "Opt-in live org: count records. Pass `objectApiName` to count every row of an object, or `soql` for a custom SELECT COUNT() query (strict shape validation). Read-only; never falls back to vault data.",
30199
30363
  inputSchema: LIVE_COUNT_INPUT_SCHEMA
30200
30364
  },
30201
30365
  {
@@ -31521,15 +31685,40 @@ var runDoctor = async (opts) => {
31521
31685
  const checks = [];
31522
31686
  const vaultRoot = resolve4(opts.cwd, DEFAULT_VAULT_ROOT);
31523
31687
  const paths = vaultPaths(vaultRoot);
31688
+ const SF_FALLBACK_PATHS = ["/usr/local/bin/sf", "/opt/homebrew/bin/sf"];
31689
+ let sfBin = "sf";
31690
+ let sfDetail = null;
31691
+ let sfOnPath = false;
31524
31692
  try {
31525
31693
  const { stdout } = await run("sf --version");
31526
- checks.push({ name: "Salesforce CLI", status: "pass", detail: stdout.trim().split("\n")[0] ?? "installed" });
31694
+ sfDetail = stdout.trim().split("\n")[0] ?? "installed";
31695
+ sfOnPath = true;
31527
31696
  } catch {
31697
+ for (const abs of SF_FALLBACK_PATHS) {
31698
+ try {
31699
+ const { stdout } = await run(`"${abs}" --version`);
31700
+ sfDetail = stdout.trim().split("\n")[0] ?? "installed";
31701
+ sfBin = `"${abs}"`;
31702
+ break;
31703
+ } catch {
31704
+ }
31705
+ }
31706
+ }
31707
+ if (sfOnPath) {
31708
+ checks.push({ name: "Salesforce CLI", status: "pass", detail: sfDetail ?? "installed" });
31709
+ } else if (sfDetail !== null) {
31710
+ checks.push({
31711
+ name: "Salesforce CLI",
31712
+ status: "warn",
31713
+ detail: `${sfDetail} \u2014 found via absolute path, not on PATH`,
31714
+ fix: "Add the Salesforce CLI directory (e.g. /usr/local/bin or /opt/homebrew/bin) to the PATH of whatever launches sfi; IDE/MCP subprocesses often do not inherit it."
31715
+ });
31716
+ } else {
31528
31717
  checks.push({
31529
31718
  name: "Salesforce CLI",
31530
31719
  status: "fail",
31531
- detail: "`sf` not found on PATH",
31532
- fix: "Install the Salesforce CLI: npm install --global @salesforce/cli"
31720
+ detail: "`sf` not found on PATH or common install locations",
31721
+ fix: "Install the Salesforce CLI (npm install --global @salesforce/cli). If it IS installed, add its directory (/usr/local/bin or /opt/homebrew/bin) to your PATH; IDE/MCP subprocesses often do not inherit it."
31533
31722
  });
31534
31723
  }
31535
31724
  const vaultInit = await pathExists2(paths.config);
@@ -31553,7 +31742,7 @@ var runDoctor = async (opts) => {
31553
31742
  });
31554
31743
  } else {
31555
31744
  try {
31556
- const { stdout } = await run(`sf org display --target-org "${targetOrg}" --json`);
31745
+ const { stdout } = await run(`${sfBin} org display --target-org "${targetOrg}" --json`);
31557
31746
  const parsed = JSON.parse(stdout);
31558
31747
  const status = parsed.result?.connectedStatus;
31559
31748
  if (status === "Connected") {
@@ -35729,8 +35918,8 @@ import { XMLParser as XMLParser19, XMLValidator as XMLValidator18 } from "fast-x
35729
35918
  var APPLICATION_FILE_SUFFIX = ".app-meta.xml";
35730
35919
  var ROOT_ELEMENT18 = "CustomApplication";
35731
35920
  var EXTRACTOR_SOURCE8 = "custom-application-extractor";
35732
- var REQUIRED_ELEMENTS4 = ["label", "navType"];
35733
- var ALLOWED_NAV_TYPE = ["Standard", "Console"];
35921
+ var REQUIRED_ELEMENTS4 = ["label"];
35922
+ var ALLOWED_NAV_TYPE = ["Standard", "Console", "Classic"];
35734
35923
  var unwrapSingle19 = (value) => Array.isArray(value) ? value[0] : value;
35735
35924
  var toArray12 = (value) => {
35736
35925
  if (value === void 0 || value === null)
@@ -35779,6 +35968,8 @@ var validateRoot10 = (parsed, path) => {
35779
35968
  if (rootObj["navType"] === void 0)
35780
35969
  rootObj["navType"] = "Standard";
35781
35970
  }
35971
+ if (rootObj["navType"] === void 0)
35972
+ rootObj["navType"] = "Classic";
35782
35973
  for (const required of REQUIRED_ELEMENTS4) {
35783
35974
  if (rootObj[required] === void 0) {
35784
35975
  return err({
@@ -35903,6 +36094,24 @@ var ROOT_ELEMENT19 = "CustomField";
35903
36094
  var FIELDS_DIR_NAME = "fields";
35904
36095
  var PICKLIST_TYPES3 = ["Picklist", "MultiselectPicklist"];
35905
36096
  var FORMULA_ELEMENT_NAME = "formula";
36097
+ var STANDARD_FIELD_TYPES = {
36098
+ Email: "Email",
36099
+ Phone: "Phone",
36100
+ Fax: "Phone",
36101
+ MobilePhone: "Phone",
36102
+ HomePhone: "Phone",
36103
+ OtherPhone: "Phone",
36104
+ AssistantPhone: "Phone",
36105
+ Website: "Url",
36106
+ CreatedDate: "DateTime",
36107
+ LastModifiedDate: "DateTime",
36108
+ SystemModstamp: "DateTime",
36109
+ LastViewedDate: "DateTime",
36110
+ LastReferencedDate: "DateTime",
36111
+ LastActivityDate: "Date",
36112
+ Birthdate: "Date",
36113
+ IsDeleted: "Checkbox"
36114
+ };
35906
36115
  var unwrapSingle20 = (value) => Array.isArray(value) ? value[0] : value;
35907
36116
  var coerceBoolean12 = (value) => {
35908
36117
  if (typeof value === "boolean")
@@ -35976,7 +36185,8 @@ var validateRoot11 = (parsed, path) => {
35976
36185
  message: "missing required element: <type>"
35977
36186
  });
35978
36187
  }
35979
- rootObj["type"] = "Unknown";
36188
+ const bareName = fullName.includes(".") ? fullName.split(".").pop() : fullName;
36189
+ rootObj["type"] = STANDARD_FIELD_TYPES[bareName] ?? "Unknown";
35980
36190
  }
35981
36191
  return ok(rootObj);
35982
36192
  };
@@ -42326,6 +42536,7 @@ var QUICK_ACTIONS_DIR_NAME = "quickActions";
42326
42536
  var REQUIRED_ELEMENTS20 = ["type"];
42327
42537
  var ALLOWED_ACTION_TYPES = [
42328
42538
  "Create",
42539
+ "Flow",
42329
42540
  "LogACall",
42330
42541
  "LightningComponent",
42331
42542
  "LightningWebComponent",
@@ -42465,6 +42676,19 @@ var buildReferencesEdge = (fromId, actionType, rootObj) => {
42465
42676
  properties: { targetKind: "page" }
42466
42677
  };
42467
42678
  }
42679
+ if (actionType === "Flow") {
42680
+ const name = optionalString23(rootObj, "flowDefinition");
42681
+ if (name === null || name.length === 0)
42682
+ return null;
42683
+ return {
42684
+ fromId,
42685
+ toId: `Flow:${name}`,
42686
+ edgeType: "references",
42687
+ confidence: "declared",
42688
+ source: EXTRACTOR_SOURCE25,
42689
+ properties: { targetKind: "flow" }
42690
+ };
42691
+ }
42468
42692
  return null;
42469
42693
  };
42470
42694
  var extractQuickAction = async (path) => {
@@ -42529,6 +42753,7 @@ var extractQuickAction = async (path) => {
42529
42753
  lightningComponent: optionalString23(rootObj, "lightningComponent"),
42530
42754
  lightningWebComponent: optionalString23(rootObj, "lightningWebComponent"),
42531
42755
  page: optionalString23(rootObj, "page"),
42756
+ flowDefinition: optionalString23(rootObj, "flowDefinition"),
42532
42757
  icon: optionalString23(rootObj, "icon"),
42533
42758
  height: optionalInteger2(rootObj, "height"),
42534
42759
  width: optionalString23(rootObj, "width")
@@ -45879,12 +46104,17 @@ var runRefresh = async (opts) => {
45879
46104
  const paths = vaultPaths(configResult.value.vaultRoot);
45880
46105
  const targetOrg = opts.targetOrg ?? configResult.value.targetOrg;
45881
46106
  const requestedTypes = parseTypeFilter(opts.types);
46107
+ const progress = opts.onProgress ?? (() => {
46108
+ });
45882
46109
  if (!opts.noPull) {
46110
+ progress(`Retrieving metadata from ${targetOrg} (this can take several minutes)...`);
45883
46111
  const pulled = await runSfRetrieve(targetOrg, paths.source, requestedTypes);
45884
46112
  if (!pulled.ok)
45885
46113
  return failed(started, pulled.error, []);
45886
46114
  }
46115
+ progress("Extracting components from retrieved source...");
45887
46116
  const walked = await walkAndExtract(paths.source, requestedTypes);
46117
+ progress(`Extracted ${walked.results.length} component file(s); building graph...`);
45888
46118
  await mkdir9(paths.graph, { recursive: true });
45889
46119
  const storeResult = await openGraph(paths.graphDb);
45890
46120
  if (!storeResult.ok) {
@@ -45953,10 +46183,13 @@ var buildCoverageEntries2 = (counts, skippedDirectories, requestedTypes, sourceR
45953
46183
  };
45954
46184
  var runWithOpenGraph = async (args) => {
45955
46185
  const { store, paths, started, targetOrg, walked, opts, requestedTypes } = args;
46186
+ const progress = opts.onProgress ?? (() => {
46187
+ });
45956
46188
  const importResult = await importExtractionResults(store, walked.results);
45957
46189
  if (!importResult.ok) {
45958
46190
  return failed(started, `importExtractionResults: ${importResult.error.message}`, walked.failures, EMPTY_COUNTS, walked.skippedDirectories);
45959
46191
  }
46192
+ progress("Rendering Markdown vault...");
45960
46193
  let counts;
45961
46194
  try {
45962
46195
  counts = await renderVault(store, paths.root);
@@ -46173,7 +46406,14 @@ var loadVaultConfig = async (cwd) => {
46173
46406
  };
46174
46407
  var METADATA_API_NAME = {
46175
46408
  VisualforcePage: "ApexPage",
46176
- VisualforceComponent: "ApexComponent"
46409
+ VisualforceComponent: "ApexComponent",
46410
+ // Sharing rules are exposed by the org as the aggregate type `SharingRules`
46411
+ // (one file per object, e.g. `Account.sharingRules-meta.xml`).
46412
+ SharingRule: "SharingRules",
46413
+ // Custom-metadata *records* (the rows of a `__mdt` type) are retrieved under
46414
+ // the `CustomMetadata` type as `{Type}.{Record}.md-meta.xml` files. The
46415
+ // `__mdt` type definitions themselves come down separately as CustomObject.
46416
+ CustomMetadataRecord: "CustomMetadata"
46177
46417
  };
46178
46418
  var toApiName = (type) => METADATA_API_NAME[type] ?? type;
46179
46419
  var SF_MAX_BUFFER = 256 * 1024 * 1024;
@@ -46242,7 +46482,8 @@ var runSfRetrieve = async (targetOrg, sourceDir, requestedTypes) => {
46242
46482
  }
46243
46483
  const { included: manifestTypes, dropped } = selectManifestTypes(requestedTypes, orgTypes);
46244
46484
  if (dropped.length > 0) {
46245
- process.stdout.write(`Skipping ${dropped.length} metadata type(s) not present in ${targetOrg}: ${dropped.join(", ")}
46485
+ const labelled = dropped.map((type) => toApiName(type) === type ? type : `${type} (${toApiName(type)})`).join(", ");
46486
+ process.stdout.write(`Skipping ${dropped.length} metadata type(s) the ${targetOrg} describe does not expose: ${labelled}
46246
46487
  `);
46247
46488
  }
46248
46489
  if (manifestTypes.length === 0) {
@@ -46344,7 +46585,11 @@ var registerRefreshCommand = (program) => {
46344
46585
  noPull: flags.pull === false,
46345
46586
  ...flags.targetOrg !== void 0 ? { targetOrg: flags.targetOrg } : {},
46346
46587
  ...flags.types !== void 0 ? { types: flags.types } : {},
46347
- ...flags.withToolingApi === true ? { withToolingApi: true } : {}
46588
+ ...flags.withToolingApi === true ? { withToolingApi: true } : {},
46589
+ // Progress goes to stderr so a multi-minute refresh isn't a silent
46590
+ // wait; stdout stays reserved for the final summary.
46591
+ onProgress: (message) => process.stderr.write(`${message}
46592
+ `)
46348
46593
  });
46349
46594
  process.stdout.write(formatRefreshSummary(result));
46350
46595
  if (result.status !== "success")
@@ -46503,7 +46748,7 @@ var registerStatusCommand = (program) => {
46503
46748
  // dist/src/program.js
46504
46749
  var readVersion = () => {
46505
46750
  if (true)
46506
- return "0.1.0";
46751
+ return "0.1.1";
46507
46752
  const pkgUrl = new URL("../../package.json", import.meta.url);
46508
46753
  const raw = readFileSync2(fileURLToPath(pkgUrl), "utf8");
46509
46754
  const parsed = JSON.parse(raw);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sf-intelligence",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Offline-first, MCP-first knowledge base for a Salesforce org. Ask about your org's metadata, dependencies, permissions, and automation — grounded in real retrieved metadata. Ships the sfi CLI and an MCP server.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://github.com/PranavNagrecha/Salesforce-Intelligence",
@@ -52,15 +52,15 @@
52
52
  "devDependencies": {
53
53
  "esbuild": "^0.28.0",
54
54
  "vitest": "^1.6.0",
55
- "@sf-intelligence/contracts": "0.1.0",
56
- "@sf-intelligence/core": "0.1.0",
57
- "@sf-intelligence/extractors": "0.1.0",
58
- "@sf-intelligence/graph": "0.1.0",
59
55
  "@sf-intelligence/mcp": "0.1.0",
56
+ "@sf-intelligence/core": "0.1.0",
57
+ "@sf-intelligence/contracts": "0.1.0",
60
58
  "@sf-intelligence/patterns": "0.1.0",
59
+ "@sf-intelligence/extractors": "0.1.0",
61
60
  "@sf-intelligence/renderers": "0.1.0",
62
- "@sf-intelligence/tooling-api": "0.1.0",
63
- "@sf-intelligence/vault": "0.1.0"
61
+ "@sf-intelligence/graph": "0.1.0",
62
+ "@sf-intelligence/vault": "0.1.0",
63
+ "@sf-intelligence/tooling-api": "0.1.0"
64
64
  },
65
65
  "scripts": {
66
66
  "build": "tsc --build && node build.mjs",