sf-intelligence 0.1.24 → 0.1.25
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/dist/index.js +63 -11
- package/package.json +1 -1
- package/server.json +2 -2
package/dist/index.js
CHANGED
|
@@ -964,6 +964,11 @@ var init_queries = __esm({
|
|
|
964
964
|
if (options?.recordTriggered === true) {
|
|
965
965
|
out += ` AND json_extract_string(properties_json, '$.triggerType') LIKE 'Record%'`;
|
|
966
966
|
}
|
|
967
|
+
if (options?.descriptionPresence === "present") {
|
|
968
|
+
out += ` AND coalesce(json_extract_string(properties_json, '$.description'), '') <> ''`;
|
|
969
|
+
} else if (options?.descriptionPresence === "absent") {
|
|
970
|
+
out += ` AND coalesce(json_extract_string(properties_json, '$.description'), '') = ''`;
|
|
971
|
+
}
|
|
967
972
|
return out;
|
|
968
973
|
};
|
|
969
974
|
parseProperties = (raw) => {
|
|
@@ -13056,8 +13061,22 @@ var init_enterprise_metadata = __esm({
|
|
|
13056
13061
|
});
|
|
13057
13062
|
return ok({ nodes: [node], edges: [...fieldRefEdges, ...childRefEdges, ...visibleToEdges] });
|
|
13058
13063
|
};
|
|
13059
|
-
extractReport = (path) => extractEnterpriseMetadata(path, {
|
|
13060
|
-
|
|
13064
|
+
extractReport = (path) => extractEnterpriseMetadata(path, {
|
|
13065
|
+
type: "Report",
|
|
13066
|
+
suffix: ".report-meta.xml",
|
|
13067
|
+
// Reports carry a top-level <description> in source. Capture it so
|
|
13068
|
+
// "which reports have no description" is answerable and get_component
|
|
13069
|
+
// can surface the report's stated purpose. Omitted when absent — the
|
|
13070
|
+
// "extracted, none present" signal (vs a not-modeled type).
|
|
13071
|
+
extraProperties: ["description"]
|
|
13072
|
+
});
|
|
13073
|
+
extractDashboard = (path) => extractEnterpriseMetadata(path, {
|
|
13074
|
+
type: "Dashboard",
|
|
13075
|
+
suffix: ".dashboard-meta.xml",
|
|
13076
|
+
// Dashboards carry a top-level <description>. Same capture rationale as
|
|
13077
|
+
// Report — omitted when absent.
|
|
13078
|
+
extraProperties: ["description"]
|
|
13079
|
+
});
|
|
13061
13080
|
extractListView = (path) => extractEnterpriseMetadata(path, {
|
|
13062
13081
|
type: "ListView",
|
|
13063
13082
|
suffix: ".listView-meta.xml",
|
|
@@ -13072,7 +13091,11 @@ var init_enterprise_metadata = __esm({
|
|
|
13072
13091
|
// Daily Summer '22") that distinguishes versioned clones from each other.
|
|
13073
13092
|
// Without this, makeNode always sets label: null and get_component has no
|
|
13074
13093
|
// human-readable display name to surface in vault Markdown.
|
|
13075
|
-
labelXmlElement: "label"
|
|
13094
|
+
labelXmlElement: "label",
|
|
13095
|
+
// ReportType XML carries a top-level <description> (nearly universal in
|
|
13096
|
+
// source). Capture it so custom report types disclose their purpose and
|
|
13097
|
+
// are queryable via missingDescription. Omitted when absent.
|
|
13098
|
+
extraProperties: ["description"]
|
|
13076
13099
|
});
|
|
13077
13100
|
extractCustomPermission = (path) => extractEnterpriseMetadata(path, { type: "CustomPermission", suffix: ".customPermission-meta.xml" });
|
|
13078
13101
|
FLEXIPAGE_PAGE_TYPES = /* @__PURE__ */ new Set([
|
|
@@ -13149,6 +13172,10 @@ var init_enterprise_metadata = __esm({
|
|
|
13149
13172
|
extractPermissionSetGroup = (path) => extractEnterpriseMetadata(path, {
|
|
13150
13173
|
type: "PermissionSetGroup",
|
|
13151
13174
|
suffix: ".permissionsetgroup-meta.xml",
|
|
13175
|
+
// PSGs carry a top-level <description>. Capture it so the group's stated
|
|
13176
|
+
// purpose is surfaced and queryable via missingDescription. Omitted when
|
|
13177
|
+
// absent.
|
|
13178
|
+
extraProperties: ["description"],
|
|
13152
13179
|
// A PSG's effective permissions are the UNION of its member permission
|
|
13153
13180
|
// sets' grants, minus the muting permission set's. Capture both so the
|
|
13154
13181
|
// permission analysis can flow god-mode / object grants through the group.
|
|
@@ -52665,7 +52692,19 @@ var init_list_components = __esm({
|
|
|
52665
52692
|
/** Flow metadata: exact match on `properties.status` (e.g. Active). */
|
|
52666
52693
|
status: z90.string().min(1).optional(),
|
|
52667
52694
|
/** Flow metadata: keep only record-triggered flows (`triggerType` starts with Record). */
|
|
52668
|
-
recordTriggered: coercedOptionalBoolean
|
|
52695
|
+
recordTriggered: coercedOptionalBoolean,
|
|
52696
|
+
/**
|
|
52697
|
+
* Keep only components that LACK a description — `properties.description` is
|
|
52698
|
+
* absent, null, or empty. Answers "which reports/objects/permission-sets have
|
|
52699
|
+
* no description". Only trustworthy for a type whose extractor captures
|
|
52700
|
+
* description; a type that carries no `<description>` in Salesforce source
|
|
52701
|
+
* (ListView, CustomPermission, MutingPermissionSet, CustomMetadata records)
|
|
52702
|
+
* will match ALL of its nodes — that means "this type has no description in
|
|
52703
|
+
* source", not "the org failed to fill them in".
|
|
52704
|
+
*/
|
|
52705
|
+
missingDescription: coercedOptionalBoolean,
|
|
52706
|
+
/** Keep only components that HAVE a non-empty `properties.description`. */
|
|
52707
|
+
hasDescription: coercedOptionalBoolean
|
|
52669
52708
|
});
|
|
52670
52709
|
listComponentsHandler = async (ctx, input2) => {
|
|
52671
52710
|
if (input2.type === void 0) {
|
|
@@ -52674,6 +52713,13 @@ var init_list_components = __esm({
|
|
|
52674
52713
|
message: "type is required for v0.1"
|
|
52675
52714
|
});
|
|
52676
52715
|
}
|
|
52716
|
+
if (input2.missingDescription === true && input2.hasDescription === true) {
|
|
52717
|
+
return err({
|
|
52718
|
+
kind: "invalid-query",
|
|
52719
|
+
message: "missingDescription and hasDescription are mutually exclusive \u2014 pass at most one."
|
|
52720
|
+
});
|
|
52721
|
+
}
|
|
52722
|
+
const descriptionPresence = input2.missingDescription === true ? "absent" : input2.hasDescription === true ? "present" : void 0;
|
|
52677
52723
|
const limit = input2.limit ?? LIST_DEFAULT_LIMIT2;
|
|
52678
52724
|
const recordTriggered = input2.recordTriggered === true;
|
|
52679
52725
|
const fingerprint = argsFingerprint({
|
|
@@ -52682,6 +52728,7 @@ var init_list_components = __esm({
|
|
|
52682
52728
|
...input2.triggerObject !== void 0 ? { triggerObject: input2.triggerObject } : {},
|
|
52683
52729
|
...input2.status !== void 0 ? { status: input2.status } : {},
|
|
52684
52730
|
...recordTriggered ? { recordTriggered: true } : {},
|
|
52731
|
+
...descriptionPresence !== void 0 ? { descriptionPresence } : {},
|
|
52685
52732
|
...Object.fromEntries(APEX_BOOLEAN_FILTERS.flatMap((k2) => input2[k2] !== void 0 ? [[k2, input2[k2]]] : []))
|
|
52686
52733
|
});
|
|
52687
52734
|
let offset = input2.offset ?? 0;
|
|
@@ -52712,7 +52759,8 @@ var init_list_components = __esm({
|
|
|
52712
52759
|
...input2.parentId !== void 0 ? { parentId: input2.parentId } : {},
|
|
52713
52760
|
...hasPropertyFilter ? { propertyEquals } : {},
|
|
52714
52761
|
...hasStringPropertyFilter ? { propertyStringEquals } : {},
|
|
52715
|
-
...recordTriggered ? { recordTriggered: true } : {}
|
|
52762
|
+
...recordTriggered ? { recordTriggered: true } : {},
|
|
52763
|
+
...descriptionPresence !== void 0 ? { descriptionPresence } : {}
|
|
52716
52764
|
};
|
|
52717
52765
|
const queryResult = await listNodesByType(ctx.graph, input2.type, {
|
|
52718
52766
|
limit,
|
|
@@ -52745,7 +52793,7 @@ var init_list_components = __esm({
|
|
|
52745
52793
|
const propertiesSlimmed = kept.some((n2) => n2.properties?.["propertiesTruncated"] === true);
|
|
52746
52794
|
const hasMore = pageNodes.length === limit || trimmed;
|
|
52747
52795
|
let retrievalHint;
|
|
52748
|
-
if (offset === 0 && pageNodes.length === 0 && !hasPropertyFilter && !hasStringPropertyFilter && !recordTriggered) {
|
|
52796
|
+
if (offset === 0 && pageNodes.length === 0 && !hasPropertyFilter && !hasStringPropertyFilter && !recordTriggered && descriptionPresence === void 0) {
|
|
52749
52797
|
const cov = summarizeCoverage(ctx.manifest, [input2.type]);
|
|
52750
52798
|
if (cov.notModeledTypes.includes(input2.type)) {
|
|
52751
52799
|
retrievalHint = `No \`${input2.type}\` in the vault \u2014 this type is NOT modeled by the current build, so its absence means "not analyzed", never "none in the org".`;
|
|
@@ -69830,7 +69878,11 @@ var init_tools = __esm({
|
|
|
69830
69878
|
hasFutureMethod: { type: "boolean" },
|
|
69831
69879
|
hasInvocableMethod: { type: "boolean" },
|
|
69832
69880
|
hasAuraEnabledMethod: { type: "boolean" },
|
|
69833
|
-
isTest: { type: "boolean" }
|
|
69881
|
+
isTest: { type: "boolean" },
|
|
69882
|
+
// Description-presence filters. Answer "which <type> have no description".
|
|
69883
|
+
// Only meaningful for a type whose extractor captures `<description>`.
|
|
69884
|
+
missingDescription: { type: "boolean" },
|
|
69885
|
+
hasDescription: { type: "boolean" }
|
|
69834
69886
|
}
|
|
69835
69887
|
});
|
|
69836
69888
|
GET_SUBGRAPH_INPUT_SCHEMA = Object.freeze({
|
|
@@ -71698,7 +71750,7 @@ var init_tools = __esm({
|
|
|
71698
71750
|
},
|
|
71699
71751
|
{
|
|
71700
71752
|
name: "sfi.list_components",
|
|
71701
|
-
description: 'List components of a given type (optionally narrowed by parentId), sorted by id. Paginated via limit/offset; `hasMore` hints at additional pages (a truncated page returns a `nextCursor` to resume). Grant-heavy rows (Profile / PermissionSet, whose nodes carry tens of KB of declarative grants) are slimmed to scalar properties \u2014 each such row is marked `properties.propertiesTruncated: true` and the page carries a top-level `propertiesSlimmed: true` \u2014 so the whole inventory fits per page; fetch full detail per component via sfi.get_component. For `type: \'ApexClass\'`, optional boolean filters list interface/async/API implementers at the DB layer (correct pagination, not a post-filtered page): `isBatchable` / `isQueueable` / `isSchedulable` / `isRestResource` / `hasFutureMethod` / `hasInvocableMethod` / `hasAuraEnabledMethod` / `isTest` \u2014 e.g. `{ type: \'ApexClass\', isBatchable: true }` returns every Batchable class. When manifest coverage for the requested `type` is not `complete`, a structured `coverageCaveat` flags the inventory as potentially incomplete (scoped refresh, errored retrieve, not modeled) \u2014 including on non-empty pages. When the FIRST page is empty, a `retrievalHint` (FRESH-02) says WHY \u2014 "none in the org" (retrieved, none found) vs "not retrieved" (a scoped refresh skipped the type \u2014 run /sfi-refresh) vs "not modeled" \u2014 so an empty list is never a silent `[]` read as "the org has none". (The hint is suppressed when a boolean filter is active, since an empty filtered result is not a coverage gap.)',
|
|
71753
|
+
description: 'List components of a given type (optionally narrowed by parentId), sorted by id. Paginated via limit/offset; `hasMore` hints at additional pages (a truncated page returns a `nextCursor` to resume). Grant-heavy rows (Profile / PermissionSet, whose nodes carry tens of KB of declarative grants) are slimmed to scalar properties \u2014 each such row is marked `properties.propertiesTruncated: true` and the page carries a top-level `propertiesSlimmed: true` \u2014 so the whole inventory fits per page; fetch full detail per component via sfi.get_component. For `type: \'ApexClass\'`, optional boolean filters list interface/async/API implementers at the DB layer (correct pagination, not a post-filtered page): `isBatchable` / `isQueueable` / `isSchedulable` / `isRestResource` / `hasFutureMethod` / `hasInvocableMethod` / `hasAuraEnabledMethod` / `isTest` \u2014 e.g. `{ type: \'ApexClass\', isBatchable: true }` returns every Batchable class. `missingDescription: true` (or `hasDescription: true`) filters to components that lack (or carry) a non-empty `properties.description`, with `totalCount` as the authoritative tally \u2014 only meaningful for a type whose extractor captures a source `<description>` (a type that carries none in source will match ALL of its nodes, meaning "no description in source", not "left blank"). When manifest coverage for the requested `type` is not `complete`, a structured `coverageCaveat` flags the inventory as potentially incomplete (scoped refresh, errored retrieve, not modeled) \u2014 including on non-empty pages. When the FIRST page is empty, a `retrievalHint` (FRESH-02) says WHY \u2014 "none in the org" (retrieved, none found) vs "not retrieved" (a scoped refresh skipped the type \u2014 run /sfi-refresh) vs "not modeled" \u2014 so an empty list is never a silent `[]` read as "the org has none". (The hint is suppressed when a boolean filter is active, since an empty filtered result is not a coverage gap.)',
|
|
71702
71754
|
inputSchema: LIST_COMPONENTS_INPUT_SCHEMA
|
|
71703
71755
|
},
|
|
71704
71756
|
{
|
|
@@ -74635,7 +74687,7 @@ var init_package_version = __esm({
|
|
|
74635
74687
|
"use strict";
|
|
74636
74688
|
readCliPackageVersion = () => {
|
|
74637
74689
|
if (true)
|
|
74638
|
-
return "0.1.
|
|
74690
|
+
return "0.1.25";
|
|
74639
74691
|
for (const rel of ["../package.json", "../../package.json"]) {
|
|
74640
74692
|
try {
|
|
74641
74693
|
const raw = readFileSync3(fileURLToPath(new URL(rel, import.meta.url)), "utf8");
|
|
@@ -144559,7 +144611,7 @@ var makeShutdownOnce = (ctx) => {
|
|
|
144559
144611
|
|
|
144560
144612
|
// dist/src/commands/demo.js
|
|
144561
144613
|
init_refresh();
|
|
144562
|
-
var buildVersion = () => true ? "0.1.
|
|
144614
|
+
var buildVersion = () => true ? "0.1.25" : "dev";
|
|
144563
144615
|
var SHUTDOWN_SIGNALS2 = ["SIGINT", "SIGTERM"];
|
|
144564
144616
|
var resolveDemoSource = () => {
|
|
144565
144617
|
let dir = dirname21(fileURLToPath2(import.meta.url));
|
|
@@ -145544,7 +145596,7 @@ init_vault_git();
|
|
|
145544
145596
|
init_watch();
|
|
145545
145597
|
var readVersion = () => {
|
|
145546
145598
|
if (true)
|
|
145547
|
-
return "0.1.
|
|
145599
|
+
return "0.1.25";
|
|
145548
145600
|
const pkgUrl = new URL("../../package.json", import.meta.url);
|
|
145549
145601
|
const raw = readFileSync6(fileURLToPath3(pkgUrl), "utf8");
|
|
145550
145602
|
const parsed = JSON.parse(raw);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sf-intelligence",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.25",
|
|
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": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"homepage": "https://salesforce-intelligence.pages.dev",
|
package/server.json
CHANGED
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
"url": "https://github.com/PranavNagrecha/Salesforce-Intelligence",
|
|
8
8
|
"source": "github"
|
|
9
9
|
},
|
|
10
|
-
"version": "0.1.
|
|
10
|
+
"version": "0.1.25",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
15
15
|
"identifier": "sf-intelligence",
|
|
16
|
-
"version": "0.1.
|
|
16
|
+
"version": "0.1.25",
|
|
17
17
|
"transport": {
|
|
18
18
|
"type": "stdio"
|
|
19
19
|
},
|