@grafema/cli 0.3.24 → 0.3.27

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 (48) hide show
  1. package/README.md +59 -45
  2. package/dist/cli.js +10 -0
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/analyzeAction.d.ts.map +1 -1
  5. package/dist/commands/analyzeAction.js +134 -3
  6. package/dist/commands/analyzeAction.js.map +1 -1
  7. package/dist/commands/doctor/checks.d.ts.map +1 -1
  8. package/dist/commands/doctor/checks.js +7 -3
  9. package/dist/commands/doctor/checks.js.map +1 -1
  10. package/dist/commands/export.d.ts +15 -0
  11. package/dist/commands/export.d.ts.map +1 -0
  12. package/dist/commands/export.js +88 -0
  13. package/dist/commands/export.js.map +1 -0
  14. package/dist/commands/exportAction.d.ts +35 -0
  15. package/dist/commands/exportAction.d.ts.map +1 -0
  16. package/dist/commands/exportAction.js +58 -0
  17. package/dist/commands/exportAction.js.map +1 -0
  18. package/dist/commands/features.d.ts +13 -0
  19. package/dist/commands/features.d.ts.map +1 -0
  20. package/dist/commands/features.js +69 -0
  21. package/dist/commands/features.js.map +1 -0
  22. package/dist/commands/featuresAction.d.ts +82 -0
  23. package/dist/commands/featuresAction.d.ts.map +1 -0
  24. package/dist/commands/featuresAction.js +139 -0
  25. package/dist/commands/featuresAction.js.map +1 -0
  26. package/dist/commands/start.d.ts +12 -0
  27. package/dist/commands/start.d.ts.map +1 -0
  28. package/dist/commands/start.js +294 -0
  29. package/dist/commands/start.js.map +1 -0
  30. package/dist/commands/trace.d.ts.map +1 -1
  31. package/dist/commands/trace.js +50 -30
  32. package/dist/commands/trace.js.map +1 -1
  33. package/dist/commands/upgrade.d.ts +3 -0
  34. package/dist/commands/upgrade.d.ts.map +1 -0
  35. package/dist/commands/upgrade.js +279 -0
  36. package/dist/commands/upgrade.js.map +1 -0
  37. package/package.json +4 -4
  38. package/src/cli.ts +11 -0
  39. package/src/commands/analyzeAction.ts +135 -2
  40. package/src/commands/doctor/checks.ts +4 -3
  41. package/src/commands/explore.tsx +29 -2
  42. package/src/commands/export.ts +102 -0
  43. package/src/commands/exportAction.ts +107 -0
  44. package/src/commands/features.ts +88 -0
  45. package/src/commands/featuresAction.ts +218 -0
  46. package/src/commands/start.ts +303 -0
  47. package/src/commands/trace.ts +49 -29
  48. package/src/commands/upgrade.ts +310 -0
@@ -0,0 +1,88 @@
1
+ /**
2
+ * `grafema export` — emit a multi-format spec from the feature graph
3
+ * (REG-1116, REG-1118).
4
+ *
5
+ * Modes shipped:
6
+ * --as openapi-3.1 → http:route features only
7
+ * --as docs-md → all FEATURE categories (single + bulk)
8
+ *
9
+ * Phase 2 formats (mcp-schema, asyncapi, json-schema, ts-declarations,
10
+ * mermaid) plug in by adding renderers to packages/util/src/exporters; the
11
+ * CLI surface here doesn't change.
12
+ */
13
+ import { Command } from 'commander';
14
+ import { resolve, join, dirname } from 'path';
15
+ import { existsSync, mkdirSync, writeFileSync } from 'fs';
16
+ import { RFDBServerBackend, RFDBClient, RENDERERS } from '@grafema/util';
17
+ import { exitWithError } from '../utils/errorFormatter.js';
18
+ import { runExport } from './exportAction.js';
19
+ const KNOWN_FORMATS = Object.keys(RENDERERS).sort().join(', ');
20
+ export const exportCommand = new Command('export')
21
+ .description('Export FEATURE entries from the graph in a chosen format')
22
+ .requiredOption('--feature <pattern>', "Feature glob (e.g. 'cli:*', 'http:*', \"cli:command:'analyze'\")")
23
+ .requiredOption('--as <format>', `Output format (${KNOWN_FORMATS})`)
24
+ .option('-o, --output <path>', 'Write output to <path> instead of stdout')
25
+ .option('-p, --project <path>', 'Project path', '.')
26
+ .addHelpText('after', `
27
+ Examples:
28
+ grafema export --feature 'http:*' --as openapi-3.1 --output api.yaml
29
+ grafema export --feature 'cli:*' --as docs-md
30
+ grafema export --feature "cli:command:'analyze'" --as docs-md
31
+
32
+ Phase 1 supports two formats: openapi-3.1 (http:route only) and
33
+ docs-md (all categories). Other formats — mcp-schema, asyncapi,
34
+ json-schema, ts-declarations, mermaid — are tracked separately.
35
+ `)
36
+ .action(async (options) => {
37
+ if (!options.feature || !options.as) {
38
+ exitWithError('Both --feature and --as are required', [
39
+ 'See: grafema export --help',
40
+ ]);
41
+ }
42
+ const projectPath = resolve(options.project);
43
+ const grafemaDir = join(projectPath, '.grafema');
44
+ const dbPath = join(grafemaDir, 'graph.rfdb');
45
+ if (!existsSync(dbPath)) {
46
+ exitWithError('No graph database found', ['Run: grafema analyze']);
47
+ }
48
+ // RFDBServerBackend negotiates protocol v3, which returns semantic edge
49
+ // dst values that don't resolve via getNode(). Connect with a plain
50
+ // RFDBClient (protocol v2) so edges preserve raw numeric ids end-to-end.
51
+ // Still use RFDBServerBackend for its auto-start side effect.
52
+ const server = new RFDBServerBackend({ dbPath, clientName: 'cli-bootstrap' });
53
+ await server.connect();
54
+ const socketPath = server.socketPath;
55
+ const rawClient = new RFDBClient(socketPath, 'export');
56
+ await rawClient.connect();
57
+ try {
58
+ await runExport({
59
+ feature: options.feature,
60
+ as: options.as,
61
+ output: options.output,
62
+ }, {
63
+ backend: rawClient,
64
+ writeText: writeOutput,
65
+ warn: (msg) => console.error(msg),
66
+ fail: (msg, code) => {
67
+ console.error(`✗ ${msg}`);
68
+ process.exit(code);
69
+ },
70
+ });
71
+ }
72
+ finally {
73
+ rawClient.close();
74
+ await server.close();
75
+ }
76
+ });
77
+ /** Default writer: stdout when path == null, otherwise file. */
78
+ function writeOutput(path, text) {
79
+ if (path === null) {
80
+ process.stdout.write(text);
81
+ return;
82
+ }
83
+ const dir = dirname(path);
84
+ if (dir && !existsSync(dir))
85
+ mkdirSync(dir, { recursive: true });
86
+ writeFileSync(path, text, 'utf8');
87
+ }
88
+ //# sourceMappingURL=export.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"export.js","sourceRoot":"","sources":["../../src/commands/export.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * `grafema export` action (REG-1116, REG-1118).
3
+ *
4
+ * Pure orchestration logic — connects feature glob → renderer, with all I/O
5
+ * surfaced as inputs (backend) and effects (writeText / log / exit). The
6
+ * Commander shell in `export.ts` plugs in concrete implementations; tests
7
+ * supply mocks.
8
+ */
9
+ import { type ExportBackendLike, type Renderer } from '@grafema/util';
10
+ export interface ExportActionOptions {
11
+ /** Feature glob — required. */
12
+ feature: string;
13
+ /** Output format — required. Must be a key of RENDERERS. */
14
+ as: string;
15
+ /** Optional output file path. When unset, stdout. */
16
+ output?: string;
17
+ }
18
+ export interface ExportActionDeps {
19
+ backend: ExportBackendLike;
20
+ /** Write file/stdout content. Receives `null` for path → stdout. */
21
+ writeText: (path: string | null, text: string) => Promise<void> | void;
22
+ /** Print to stderr (for diagnostic notes that should not contaminate stdout). */
23
+ warn: (msg: string) => void;
24
+ /** Reported when an unrecoverable error happens — caller decides exit code. */
25
+ fail: (msg: string, code: number) => never;
26
+ /** Renderers map — defaults to the package-shipped RENDERERS. */
27
+ renderers?: Record<string, Renderer>;
28
+ }
29
+ /**
30
+ * Run the export action. Returns the rendered text on success — convenient
31
+ * for tests that don't want to mock writeText. The deps' writeText is also
32
+ * invoked so production callers don't need to handle the return value.
33
+ */
34
+ export declare function runExport(options: ExportActionOptions, deps: ExportActionDeps): Promise<string>;
35
+ //# sourceMappingURL=exportAction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exportAction.d.ts","sourceRoot":"","sources":["../../src/commands/exportAction.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAKL,KAAK,iBAAiB,EAEtB,KAAK,QAAQ,EACd,MAAM,eAAe,CAAC;AAEvB,MAAM,WAAW,mBAAmB;IAClC,+BAA+B;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,4DAA4D;IAC5D,EAAE,EAAE,MAAM,CAAC;IACX,qDAAqD;IACrD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,iBAAiB,CAAC;IAC3B,oEAAoE;IACpE,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACvE,iFAAiF;IACjF,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,+EAA+E;IAC/E,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,KAAK,CAAC;IAC3C,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;CACtC;AAED;;;;GAIG;AACH,wBAAsB,SAAS,CAC7B,OAAO,EAAE,mBAAmB,EAC5B,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC,MAAM,CAAC,CA2DjB"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * `grafema export` action (REG-1116, REG-1118).
3
+ *
4
+ * Pure orchestration logic — connects feature glob → renderer, with all I/O
5
+ * surfaced as inputs (backend) and effects (writeText / log / exit). The
6
+ * Commander shell in `export.ts` plugs in concrete implementations; tests
7
+ * supply mocks.
8
+ */
9
+ import { collectFeatureSnapshots, parseFeaturePattern, resolveCategories, RENDERERS, } from '@grafema/util';
10
+ /**
11
+ * Run the export action. Returns the rendered text on success — convenient
12
+ * for tests that don't want to mock writeText. The deps' writeText is also
13
+ * invoked so production callers don't need to handle the return value.
14
+ */
15
+ export async function runExport(options, deps) {
16
+ const renderers = deps.renderers ?? RENDERERS;
17
+ const renderer = renderers[options.as];
18
+ if (!renderer) {
19
+ const known = Object.keys(renderers).sort().join(', ');
20
+ deps.fail(`Unknown format '${options.as}'. Known formats: ${known}.`, 2);
21
+ }
22
+ const parsed = parseFeaturePattern(options.feature);
23
+ if (!parsed) {
24
+ deps.fail(`Invalid --feature pattern: '${options.feature}'.`, 2);
25
+ }
26
+ // Validate format-category compatibility before doing any graph work.
27
+ const categories = resolveCategories(parsed.categoryGlob);
28
+ if (categories.length === 0) {
29
+ deps.fail(`Feature pattern '${options.feature}' matches no known category.`, 2);
30
+ }
31
+ const unsupported = categories.filter(c => !renderer.supports(c));
32
+ if (unsupported.length > 0 && unsupported.length === categories.length) {
33
+ deps.fail(`Format '${options.as}' does not support category '${unsupported[0]}'. ` +
34
+ `Try '--as docs-md' or narrow the pattern.`, 2);
35
+ }
36
+ const snapshots = await collectFeatureSnapshots(deps.backend, options.feature);
37
+ // If the renderer rejects some categories, drop those snapshots before
38
+ // rendering. Warn so the user sees the gap.
39
+ const accepted = [];
40
+ let droppedForCategory = 0;
41
+ for (const s of snapshots) {
42
+ if (renderer.supports(s.category))
43
+ accepted.push(s);
44
+ else
45
+ droppedForCategory++;
46
+ }
47
+ if (droppedForCategory > 0) {
48
+ deps.warn(`Skipping ${droppedForCategory} feature${droppedForCategory === 1 ? '' : 's'} ` +
49
+ `unsupported by --as ${options.as}.`);
50
+ }
51
+ if (accepted.length === 0) {
52
+ deps.warn(`No features matched '${options.feature}' for format '${options.as}'.`);
53
+ }
54
+ const text = renderer.render(accepted);
55
+ await deps.writeText(options.output ?? null, text);
56
+ return text;
57
+ }
58
+ //# sourceMappingURL=exportAction.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exportAction.js","sourceRoot":"","sources":["../../src/commands/exportAction.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * `grafema features` — surface FEATURE-level cross-modality insights.
3
+ *
4
+ * Currently exposes one mode:
5
+ * --duplicates List clusters of FEATUREs whose entry-points share an
6
+ * identical BEHAVIOR (same forward-slice hash).
7
+ *
8
+ * Other modes (--by-effect, --by-domain, …) are anticipated but not in scope
9
+ * for REG-1119.
10
+ */
11
+ import { Command } from 'commander';
12
+ export declare const featuresCommand: Command;
13
+ //# sourceMappingURL=features.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"features.d.ts","sourceRoot":"","sources":["../../src/commands/features.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAmBpC,eAAO,MAAM,eAAe,SAkDxB,CAAC"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * `grafema features` — surface FEATURE-level cross-modality insights.
3
+ *
4
+ * Currently exposes one mode:
5
+ * --duplicates List clusters of FEATUREs whose entry-points share an
6
+ * identical BEHAVIOR (same forward-slice hash).
7
+ *
8
+ * Other modes (--by-effect, --by-domain, …) are anticipated but not in scope
9
+ * for REG-1119.
10
+ */
11
+ import { Command } from 'commander';
12
+ import { resolve, join } from 'path';
13
+ import { existsSync } from 'fs';
14
+ import { RFDBServerBackend } from '@grafema/util';
15
+ import { exitWithError } from '../utils/errorFormatter.js';
16
+ import { findSharedBehaviorClusters, formatSharedBehaviorClusters, } from './featuresAction.js';
17
+ export const featuresCommand = new Command('features')
18
+ .description('List FEATURE-level cross-modality insights (e.g. duplicate behaviors)')
19
+ .option('-p, --project <path>', 'Project path', '.')
20
+ .option('-j, --json', 'Output as JSON')
21
+ .option('-d, --duplicates', 'List clusters of FEATUREs that share a BEHAVIOR')
22
+ .option('--min-cluster-size <n>', 'Minimum features per cluster (default: 2)', '2')
23
+ .option('--limit <n>', 'Maximum clusters to return (default: 100)', '100')
24
+ .addHelpText('after', `
25
+ Examples:
26
+ grafema features --duplicates List FEATUREs with shared behaviors
27
+ grafema features --duplicates --json Same, machine-readable JSON
28
+ grafema features -d --min-cluster-size 3 Only clusters with >=3 features
29
+ `)
30
+ .action(async (options) => {
31
+ if (!options.duplicates) {
32
+ exitWithError('No subcommand selected', [
33
+ 'Try: grafema features --duplicates',
34
+ 'See: grafema features --help',
35
+ ]);
36
+ }
37
+ const projectPath = resolve(options.project);
38
+ const grafemaDir = join(projectPath, '.grafema');
39
+ const dbPath = join(grafemaDir, 'graph.rfdb');
40
+ if (!existsSync(dbPath)) {
41
+ exitWithError('No graph database found', ['Run: grafema analyze']);
42
+ }
43
+ const minClusterSize = parsePositiveInt(options.minClusterSize, 2);
44
+ const limit = parsePositiveInt(options.limit, 100);
45
+ const backend = new RFDBServerBackend({ dbPath, clientName: 'cli' });
46
+ await backend.connect();
47
+ try {
48
+ // RFDBServerBackend matches FeaturesBackendLike at runtime.
49
+ const clusters = await findSharedBehaviorClusters(backend, { minClusterSize, limit });
50
+ if (options.json) {
51
+ console.log(JSON.stringify(clusters, null, 2));
52
+ }
53
+ else {
54
+ console.log(formatSharedBehaviorClusters(clusters));
55
+ }
56
+ }
57
+ finally {
58
+ await backend.close();
59
+ }
60
+ });
61
+ function parsePositiveInt(raw, fallback) {
62
+ if (!raw)
63
+ return fallback;
64
+ const n = Number.parseInt(raw, 10);
65
+ if (!Number.isFinite(n) || n <= 0)
66
+ return fallback;
67
+ return n;
68
+ }
69
+ //# sourceMappingURL=features.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"features.js","sourceRoot":"","sources":["../../src/commands/features.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Features command action — surface FEATURE-level cross-modality insights.
3
+ *
4
+ * Currently implements --duplicates: groups BEHAVIOR nodes by their
5
+ * `metadata.hash` and lists every cluster of size >= 2. Each cluster shows the
6
+ * feature ENTRY_POINTs (incoming `IMPLEMENTED_BY` edges) that share the
7
+ * implementation.
8
+ *
9
+ * Surfaces the data emitted by `behaviorEnricher` Pass 2: SHARES_BEHAVIOR_WITH
10
+ * edges already exist, but they're not user-facing. This subcommand makes the
11
+ * insight visible from the CLI ("this CLI command and that MCP tool are thin
12
+ * wrappers around the same library function").
13
+ *
14
+ * REG-1119.
15
+ */
16
+ import type { EdgeType } from '@grafema/types';
17
+ /**
18
+ * Minimal backend interface — matches the subset of RFDBServerBackend
19
+ * (and the graph-handlers' GraphBackendLike) that we need. Lets us test with
20
+ * a simple in-memory mock without requiring a live RFDB server.
21
+ */
22
+ export interface FeaturesBackendLike {
23
+ queryNodes(query: {
24
+ type?: string;
25
+ nodeType?: string;
26
+ }): AsyncIterable<Record<string, unknown>>;
27
+ getNode(id: string): Promise<Record<string, unknown> | null>;
28
+ getIncomingEdges(nodeId: string, edgeTypes?: EdgeType[] | null): Promise<Array<{
29
+ src: string;
30
+ dst: string;
31
+ type: string;
32
+ metadata?: Record<string, unknown> | undefined;
33
+ }>>;
34
+ }
35
+ /** A FEATURE participating in a shared-behavior cluster. */
36
+ export interface SharedFeatureRef {
37
+ /** Feature semantic id. */
38
+ id: string;
39
+ /** Feature node type — e.g. `cli:command`, `mcp:tool`, `vscode:command`. */
40
+ type: string;
41
+ /** Feature name (e.g. `analyze`). */
42
+ name: string;
43
+ /** Source file (may be empty for synthetic features). */
44
+ file: string;
45
+ }
46
+ /** A group of FEATUREs whose behavior hashes are identical. */
47
+ export interface SharedBehaviorCluster {
48
+ /** sha256 hash from BEHAVIOR.metadata.hash — the cluster key. */
49
+ hash: string;
50
+ /** Effects array carried on the shared BEHAVIOR. */
51
+ effects: string[];
52
+ /** Forward-slice size of the shared behavior. */
53
+ coreNodeCount: number;
54
+ /** Features that implement the same behavior. */
55
+ features: SharedFeatureRef[];
56
+ }
57
+ export interface FindSharedBehaviorOptions {
58
+ /** Minimum cluster size to include. Default 2. */
59
+ minClusterSize?: number;
60
+ /** Maximum number of clusters to return. Default 100. */
61
+ limit?: number;
62
+ }
63
+ /**
64
+ * Core algorithm — testable, accepts any backend implementing
65
+ * `FeaturesBackendLike`.
66
+ *
67
+ * Steps:
68
+ * 1. Iterate every BEHAVIOR node, read its `hash` + `effects` + `coreNodeCount`.
69
+ * 2. Bucket behaviors by hash.
70
+ * 3. For each bucket of size >= minClusterSize, walk incoming `IMPLEMENTED_BY`
71
+ * edges to enumerate the FEATUREs.
72
+ *
73
+ * Returns clusters sorted by size (desc), then by hash (asc, deterministic).
74
+ */
75
+ export declare function findSharedBehaviorClusters(backend: FeaturesBackendLike, options?: FindSharedBehaviorOptions): Promise<SharedBehaviorCluster[]>;
76
+ /**
77
+ * Format clusters as a human-readable text report. Mirrors the symmetric MCP
78
+ * handler output: same field ordering, same labels. JSON output is the same
79
+ * structured array.
80
+ */
81
+ export declare function formatSharedBehaviorClusters(clusters: SharedBehaviorCluster[]): string;
82
+ //# sourceMappingURL=featuresAction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"featuresAction.d.ts","sourceRoot":"","sources":["../../src/commands/featuresAction.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,UAAU,CAAC,KAAK,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,aAAa,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAChG,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;IAC7D,gBAAgB,CACd,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,QAAQ,EAAE,GAAG,IAAI,GAC5B,OAAO,CAAC,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAA;KAAE,CAAC,CAAC,CAAC;CAC/G;AAED,4DAA4D;AAC5D,MAAM,WAAW,gBAAgB;IAC/B,2BAA2B;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,4EAA4E;IAC5E,IAAI,EAAE,MAAM,CAAC;IACb,qCAAqC;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAC;CACd;AAED,+DAA+D;AAC/D,MAAM,WAAW,qBAAqB;IACpC,iEAAiE;IACjE,IAAI,EAAE,MAAM,CAAC;IACb,oDAAoD;IACpD,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,iDAAiD;IACjD,aAAa,EAAE,MAAM,CAAC;IACtB,iDAAiD;IACjD,QAAQ,EAAE,gBAAgB,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,yBAAyB;IACxC,kDAAkD;IAClD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAKD;;;;;;;;;;;GAWG;AACH,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,mBAAmB,EAC5B,OAAO,GAAE,yBAA8B,GACtC,OAAO,CAAC,qBAAqB,EAAE,CAAC,CAiFlC;AAED;;;;GAIG;AACH,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,qBAAqB,EAAE,GAAG,MAAM,CAsBtF"}
@@ -0,0 +1,139 @@
1
+ const DEFAULT_MIN_CLUSTER_SIZE = 2;
2
+ const DEFAULT_LIMIT = 100;
3
+ /**
4
+ * Core algorithm — testable, accepts any backend implementing
5
+ * `FeaturesBackendLike`.
6
+ *
7
+ * Steps:
8
+ * 1. Iterate every BEHAVIOR node, read its `hash` + `effects` + `coreNodeCount`.
9
+ * 2. Bucket behaviors by hash.
10
+ * 3. For each bucket of size >= minClusterSize, walk incoming `IMPLEMENTED_BY`
11
+ * edges to enumerate the FEATUREs.
12
+ *
13
+ * Returns clusters sorted by size (desc), then by hash (asc, deterministic).
14
+ */
15
+ export async function findSharedBehaviorClusters(backend, options = {}) {
16
+ const minClusterSize = Math.max(2, options.minClusterSize ?? DEFAULT_MIN_CLUSTER_SIZE);
17
+ const limit = Math.max(1, options.limit ?? DEFAULT_LIMIT);
18
+ const behaviors = [];
19
+ for await (const node of backend.queryNodes({ type: 'BEHAVIOR' })) {
20
+ const id = String(node.id ?? '');
21
+ if (!id)
22
+ continue;
23
+ const hash = readString(node.hash);
24
+ if (!hash)
25
+ continue;
26
+ const effects = readStringArray(node.effects);
27
+ const coreNodeCount = readNumber(node.coreNodeCount) ?? 0;
28
+ behaviors.push({ id, hash, effects, coreNodeCount });
29
+ }
30
+ // 2. Group by hash.
31
+ const byHash = new Map();
32
+ for (const b of behaviors) {
33
+ let bucket = byHash.get(b.hash);
34
+ if (!bucket) {
35
+ bucket = [];
36
+ byHash.set(b.hash, bucket);
37
+ }
38
+ bucket.push(b);
39
+ }
40
+ // 3. For each multi-behavior bucket, expand incoming IMPLEMENTED_BY edges
41
+ // into FEATURE refs.
42
+ const clusters = [];
43
+ for (const [hash, bucket] of byHash) {
44
+ if (bucket.length < minClusterSize)
45
+ continue;
46
+ const features = [];
47
+ const seenFeatureIds = new Set();
48
+ for (const beh of bucket) {
49
+ const edges = await backend.getIncomingEdges(beh.id, ['IMPLEMENTED_BY']);
50
+ for (const edge of edges) {
51
+ const featureId = String(edge.src);
52
+ if (!featureId || seenFeatureIds.has(featureId))
53
+ continue;
54
+ seenFeatureIds.add(featureId);
55
+ const node = await backend.getNode(featureId);
56
+ if (!node)
57
+ continue;
58
+ features.push({
59
+ id: featureId,
60
+ type: readString(node.type) ?? 'UNKNOWN',
61
+ name: readString(node.name) ?? '',
62
+ file: readString(node.file) ?? '',
63
+ });
64
+ }
65
+ }
66
+ // De-dup may have dropped behaviors back below threshold (e.g. multiple
67
+ // BEHAVIOR rows from the same FEATURE). Re-check on the resolved feature
68
+ // count.
69
+ if (features.length < minClusterSize)
70
+ continue;
71
+ // Stable order of features within a cluster: by type then name.
72
+ features.sort((a, b) => (a.type === b.type ? a.name.localeCompare(b.name) : a.type.localeCompare(b.type)));
73
+ // Take effects + coreNodeCount from the first behavior — they're identical
74
+ // for behaviors with the same hash by construction.
75
+ clusters.push({
76
+ hash,
77
+ effects: bucket[0].effects,
78
+ coreNodeCount: bucket[0].coreNodeCount,
79
+ features,
80
+ });
81
+ }
82
+ // Sort clusters: largest first, hash ascending for tie-break (deterministic).
83
+ clusters.sort((a, b) => (b.features.length - a.features.length) || a.hash.localeCompare(b.hash));
84
+ return clusters.slice(0, limit);
85
+ }
86
+ /**
87
+ * Format clusters as a human-readable text report. Mirrors the symmetric MCP
88
+ * handler output: same field ordering, same labels. JSON output is the same
89
+ * structured array.
90
+ */
91
+ export function formatSharedBehaviorClusters(clusters) {
92
+ if (clusters.length === 0) {
93
+ return 'No FEATUREs share a BEHAVIOR (each entry-point has a unique implementation).';
94
+ }
95
+ const lines = [];
96
+ lines.push(`Shared-behavior clusters: ${clusters.length}`);
97
+ lines.push('='.repeat(40));
98
+ for (let i = 0; i < clusters.length; i++) {
99
+ const c = clusters[i];
100
+ lines.push('');
101
+ lines.push(`Cluster ${i + 1} — ${c.features.length} feature(s) share behavior`);
102
+ lines.push(` hash: ${c.hash.slice(0, 16)}…`);
103
+ lines.push(` coreNodeCount: ${c.coreNodeCount}`);
104
+ lines.push(` effects: ${c.effects.length === 0 ? '(none)' : c.effects.join(', ')}`);
105
+ lines.push(' features:');
106
+ for (const f of c.features) {
107
+ const file = f.file ? ` [${f.file}]` : '';
108
+ lines.push(` - ${f.type} ${f.name}${file}`);
109
+ }
110
+ }
111
+ return lines.join('\n');
112
+ }
113
+ // ---------------------------------------------------------------------------
114
+ // Internal helpers — defensive parsers tolerating both string-encoded JSON
115
+ // metadata (from raw client) and already-parsed values (from RFDBServerBackend
116
+ // _parseNode).
117
+ // ---------------------------------------------------------------------------
118
+ function readString(v) {
119
+ return typeof v === 'string' ? v : undefined;
120
+ }
121
+ function readNumber(v) {
122
+ return typeof v === 'number' && Number.isFinite(v) ? v : undefined;
123
+ }
124
+ function readStringArray(v) {
125
+ if (Array.isArray(v))
126
+ return v.filter((x) => typeof x === 'string');
127
+ if (typeof v === 'string') {
128
+ try {
129
+ const parsed = JSON.parse(v);
130
+ if (Array.isArray(parsed))
131
+ return parsed.filter((x) => typeof x === 'string');
132
+ }
133
+ catch {
134
+ // not JSON
135
+ }
136
+ }
137
+ return [];
138
+ }
139
+ //# sourceMappingURL=featuresAction.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"featuresAction.js","sourceRoot":"","sources":["../../src/commands/featuresAction.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * `grafema start` — unified command to bring up the entire Grafema stack.
3
+ *
4
+ * Starts RFDB server (with HTTP) and prints connection info.
5
+ * Foreground by default; `--background` detaches.
6
+ *
7
+ * `grafema stop` — graceful shutdown of everything.
8
+ */
9
+ import { Command } from 'commander';
10
+ export declare const startCommand: Command;
11
+ export declare const stopCommand: Command;
12
+ //# sourceMappingURL=start.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../src/commands/start.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA0FpC,eAAO,MAAM,YAAY,SAgKrB,CAAC;AAIL,eAAO,MAAM,WAAW,SAuCpB,CAAC"}