@graphit/cli 0.2.270 → 0.2.306

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 (37) hide show
  1. package/.claude-plugin/marketplace.json +3 -3
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.codex-plugin/plugin.json +1 -1
  4. package/bin/graphit +1 -1
  5. package/bin/graphit.ps1 +1 -1
  6. package/dist/commands/connector.js +4 -2
  7. package/dist/commands/connector.js.map +1 -1
  8. package/dist/commands/dashboard.js +24 -11
  9. package/dist/commands/dashboard.js.map +1 -1
  10. package/dist/commands/ds/refresh-history.js +2 -1
  11. package/dist/commands/ds/refresh-history.js.map +1 -1
  12. package/dist/commands/ds/ui-only.js +10 -4
  13. package/dist/commands/ds/ui-only.js.map +1 -1
  14. package/dist/commands/ds.js +8 -4
  15. package/dist/commands/ds.js.map +1 -1
  16. package/dist/commands/governance.js +2 -2
  17. package/dist/commands/governance.js.map +1 -1
  18. package/dist/commands/kb-create.js +8 -8
  19. package/dist/commands/kb-create.js.map +1 -1
  20. package/dist/commands/kb-delete.js +4 -1
  21. package/dist/commands/kb-delete.js.map +1 -1
  22. package/dist/commands/kb-read.js +22 -10
  23. package/dist/commands/kb-read.js.map +1 -1
  24. package/dist/commands/kb-shared.d.ts +3 -0
  25. package/dist/commands/kb-shared.js +12 -0
  26. package/dist/commands/kb-shared.js.map +1 -1
  27. package/dist/commands/kb-update.js +19 -10
  28. package/dist/commands/kb-update.js.map +1 -1
  29. package/dist/commands/query.js +2 -1
  30. package/dist/commands/query.js.map +1 -1
  31. package/package.json +4 -4
  32. package/scripts/commander-walk.mjs +154 -0
  33. package/scripts/generate-commands-doc.mjs +26 -95
  34. package/scripts/generate-tool-manifest.mjs +613 -0
  35. package/scripts/verb-policy-source.json +555 -0
  36. package/skills/graphit/SKILL.md +2 -2
  37. package/skills/graphit/VERSION.json +1 -1
@@ -0,0 +1,154 @@
1
+ // The one Commander walk. Both generators consume it - the SKILL.md command
2
+ // table (generate-commands-doc.mjs) and the platform-agent tool manifest
3
+ // (generate-tool-manifest.mjs) - so a command, flag or description can never
4
+ // mean two different things on the two surfaces.
5
+ //
6
+ // Project #275: the in-app agent's tool vocabulary IS the CLI vocabulary. A
7
+ // second walk would be a second source of truth, which is the drift this
8
+ // project exists to remove.
9
+ //
10
+ // Everything below `collectVerbs` operates on the walk's own plain records, not
11
+ // on Commander objects. A field this file does not capture is a field neither
12
+ // generator can see - `mandatory` was dropped once and published 36 required
13
+ // flags as optional, so add to the record rather than reaching past it.
14
+
15
+ import { existsSync, readdirSync } from "node:fs";
16
+ import { join } from "node:path";
17
+ import { fileURLToPath, pathToFileURL } from "node:url";
18
+ import { Command } from "commander";
19
+
20
+ export const cliRoot =
21
+ process.env.GRAPHIT_CLI_ROOT ??
22
+ join(fileURLToPath(new URL(".", import.meta.url)), "..");
23
+
24
+ // Mirrors index.ts registration order; groups not listed are appended alphabetically.
25
+ export const GROUP_ORDER = [
26
+ "auth", "status", "kb", "query", "metadata", "ds", "dashboard",
27
+ "connector", "governance", "team", "plugin", "setup",
28
+ ];
29
+
30
+ /**
31
+ * Assemble a throwaway program by importing only the dist/ registrars.
32
+ *
33
+ * NEVER import dist/index.js - module load fires the update banner,
34
+ * program.parse(), and a network update check.
35
+ */
36
+ export async function buildProgram() {
37
+ const distCommandsDir = join(cliRoot, "dist", "commands");
38
+ if (!existsSync(distCommandsDir)) {
39
+ throw new Error(
40
+ `dist/commands not found at ${distCommandsDir} - run "npm run build" first`,
41
+ );
42
+ }
43
+ const program = new Command();
44
+ program.name("graphit");
45
+ const files = readdirSync(distCommandsDir)
46
+ .filter((f) => f.endsWith(".js"))
47
+ .sort(); // deterministic import order
48
+ for (const file of files) {
49
+ const mod = await import(pathToFileURL(join(distCommandsDir, file)).href);
50
+ for (const [name, value] of Object.entries(mod)) {
51
+ if (typeof value === "function" && /^register/.test(name)) {
52
+ value(program);
53
+ }
54
+ }
55
+ }
56
+ return program;
57
+ }
58
+
59
+ /** `<id> [ids...]` from the walk's structured arguments. */
60
+ export function formatArgs(args) {
61
+ return args
62
+ .map((arg) => {
63
+ const name = arg.name + (arg.variadic ? "..." : "");
64
+ return arg.required ? `<${name}>` : `[${name}]`;
65
+ })
66
+ .join(" ");
67
+ }
68
+
69
+ /** `--limit --verified` from the walk's structured options. */
70
+ export function formatFlags(options) {
71
+ return options.map((opt) => opt.long || opt.flags).filter(Boolean).join(" ");
72
+ }
73
+
74
+ export function orderGroups(names) {
75
+ return [...names].sort((a, b) => {
76
+ const ia = GROUP_ORDER.indexOf(a);
77
+ const ib = GROUP_ORDER.indexOf(b);
78
+ if (ia === -1 && ib === -1) return a.localeCompare(b);
79
+ if (ia === -1) return 1;
80
+ if (ib === -1) return -1;
81
+ return ia - ib;
82
+ });
83
+ }
84
+
85
+ // Hidden options (hideHelp) are unpublished by definition - deprecated or
86
+ // internal flags must not be taught to agents on either surface.
87
+ const visibleOptions = (cmd) => (cmd.options ?? []).filter((opt) => !opt.hidden);
88
+
89
+ // A command earns its own row if it is a leaf OR it has its own args/options
90
+ // (e.g. `query <sql>` both takes an arg and has the `metadata` sibling group).
91
+ const hasOwnInvocation = (cmd) =>
92
+ (cmd.registeredArguments ?? []).length > 0 || (cmd.options ?? []).length > 0;
93
+
94
+ /**
95
+ * Every invocable command in the tree, in group order then registration order.
96
+ *
97
+ * `action` is the path below the group ("create metric", "update-html", "" for
98
+ * a group that is itself invocable such as `query` or `status`).
99
+ */
100
+ export function collectVerbs(program) {
101
+ const verbs = [];
102
+
103
+ const visit = (cmd, group, path) => {
104
+ const subs = cmd.commands ?? [];
105
+ if (subs.length === 0 || hasOwnInvocation(cmd)) {
106
+ verbs.push({
107
+ group,
108
+ action: path.slice(group.length).trim(),
109
+ command: path,
110
+ description: cmd.description() ?? "",
111
+ args: (cmd.registeredArguments ?? []).map((arg) => ({
112
+ name: arg.name(),
113
+ description: arg.description ?? "",
114
+ required: Boolean(arg.required),
115
+ variadic: Boolean(arg.variadic),
116
+ })),
117
+ options: visibleOptions(cmd).map((opt) => ({
118
+ flags: opt.flags,
119
+ long: opt.long ?? "",
120
+ short: opt.short ?? "",
121
+ description: opt.description ?? "",
122
+ // A value-taking option renders as `--limit <n>`; a boolean switch has
123
+ // no placeholder. Commander exposes both as `required`/`optional` on
124
+ // the option's ARGUMENT, never on the option itself - `mandatory` is
125
+ // the separate flag meaning ".requiredOption(), the CLI rejects the
126
+ // command without it".
127
+ takesValue: Boolean(opt.required || opt.optional),
128
+ valueOptional: Boolean(opt.optional),
129
+ mandatory: Boolean(opt.mandatory),
130
+ negated: Boolean(opt.negate),
131
+ variadic: Boolean(opt.variadic),
132
+ defaultValue: opt.defaultValue,
133
+ choices: opt.argChoices ?? null,
134
+ })),
135
+ });
136
+ }
137
+ for (const sub of subs) {
138
+ visit(sub, group, `${path} ${sub.name()}`.trim());
139
+ }
140
+ };
141
+
142
+ for (const name of orderGroups(program.commands.map((c) => c.name()))) {
143
+ const top = program.commands.find((c) => c.name() === name);
144
+ visit(top, name, name);
145
+ }
146
+ return verbs;
147
+ }
148
+
149
+ /** Group name -> its top-level `.description()`. */
150
+ export function collectGroupDescriptions(program) {
151
+ const out = new Map();
152
+ for (const top of program.commands) out.set(top.name(), top.description() ?? "");
153
+ return out;
154
+ }
@@ -9,123 +9,54 @@
9
9
  //
10
10
  // Requires a prior `npm run build` (it imports the compiled registrars from dist/).
11
11
 
12
- import { existsSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
12
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
13
13
  import { join } from "node:path";
14
- import { fileURLToPath, pathToFileURL } from "node:url";
15
- import { Command } from "commander";
14
+ import {
15
+ buildProgram,
16
+ cliRoot,
17
+ collectGroupDescriptions,
18
+ collectVerbs,
19
+ formatArgs,
20
+ formatFlags,
21
+ } from "./commander-walk.mjs";
16
22
 
17
23
  const checkOnly = process.argv.includes("--check");
18
24
  const toStdout = process.argv.includes("--stdout");
19
25
 
20
- const cliRoot =
21
- process.env.GRAPHIT_CLI_ROOT ??
22
- join(fileURLToPath(new URL(".", import.meta.url)), "..");
23
- const distCommandsDir = join(cliRoot, "dist", "commands");
24
26
  const skillPath = join(cliRoot, "skills", "graphit", "SKILL.md");
25
27
 
26
28
  const START = "<!-- COMMANDS:START -->";
27
29
  const END = "<!-- COMMANDS:END -->";
28
30
 
29
- // Mirrors index.ts registration order; groups not listed are appended alphabetically.
30
- const GROUP_ORDER = [
31
- "auth", "status", "kb", "query", "metadata", "ds", "dashboard",
32
- "connector", "governance", "team", "plugin", "setup",
33
- ];
34
-
35
- // Assemble a throwaway program by importing only the dist/ registrars (never
36
- // dist/index.js - that fires the update banner + program.parse() at load).
37
- async function buildProgram() {
38
- if (!existsSync(distCommandsDir)) {
39
- throw new Error(
40
- `dist/commands not found at ${distCommandsDir} - run "npm run build" first`,
41
- );
42
- }
43
- const program = new Command();
44
- program.name("graphit");
45
- const files = readdirSync(distCommandsDir)
46
- .filter((f) => f.endsWith(".js"))
47
- .sort(); // deterministic import order
48
- for (const file of files) {
49
- const mod = await import(pathToFileURL(join(distCommandsDir, file)).href);
50
- for (const [name, value] of Object.entries(mod)) {
51
- if (typeof value === "function" && /^register/.test(name)) {
52
- value(program);
53
- }
54
- }
55
- }
56
- return program;
57
- }
58
-
59
31
  function esc(text) {
60
32
  return String(text ?? "").replace(/\r?\n/g, " ").replace(/\|/g, "\\|").trim();
61
33
  }
62
34
 
63
- function formatArgs(cmd) {
64
- return (cmd.registeredArguments ?? [])
65
- .map((arg) => {
66
- const name = arg.name() + (arg.variadic ? "..." : "");
67
- return arg.required ? `<${name}>` : `[${name}]`;
68
- })
69
- .join(" ");
70
- }
71
-
72
- function formatFlags(cmd) {
73
- return (cmd.options ?? [])
74
- // Hidden options (hideHelp) are unpublished by definition - deprecated or
75
- // internal flags must not be taught to agents via the command table.
76
- .filter((opt) => !opt.hidden)
77
- .map((opt) => opt.long ?? opt.flags)
78
- .filter(Boolean)
79
- .join(" ");
80
- }
81
-
82
- // A command earns its own row if it is a leaf OR it has its own args/options
83
- // (e.g. `query <sql>` both takes an arg and has the `metadata` sibling group).
84
- function hasOwnInvocation(cmd) {
85
- return (
86
- (cmd.registeredArguments ?? []).length > 0 ||
87
- (cmd.options ?? []).length > 0
88
- );
89
- }
90
-
91
- function collectRows(cmd, path, rows) {
92
- const subs = cmd.commands ?? [];
93
- if (subs.length === 0 || hasOwnInvocation(cmd)) {
94
- rows.push({
95
- command: `${path}${formatArgs(cmd) ? ` ${formatArgs(cmd)}` : ""}`,
96
- description: cmd.description(),
97
- flags: formatFlags(cmd),
98
- });
99
- }
100
- for (const sub of subs) {
101
- collectRows(sub, `${path} ${sub.name()}`.trim(), rows);
102
- }
103
- }
104
-
105
- function orderGroups(names) {
106
- return [...names].sort((a, b) => {
107
- const ia = GROUP_ORDER.indexOf(a);
108
- const ib = GROUP_ORDER.indexOf(b);
109
- if (ia === -1 && ib === -1) return a.localeCompare(b);
110
- if (ia === -1) return 1;
111
- if (ib === -1) return -1;
112
- return ia - ib;
113
- });
114
- }
115
-
116
35
  function renderBlock(program) {
36
+ const groupDescriptions = collectGroupDescriptions(program);
117
37
  const groups = new Map();
118
- for (const top of program.commands) {
119
- const rows = [];
120
- collectRows(top, top.name(), rows);
121
- groups.set(top.name(), { description: top.description(), rows });
38
+ // collectVerbs already returns groups in GROUP_ORDER, so insertion order here
39
+ // is the render order - no second sort.
40
+ for (const verb of collectVerbs(program)) {
41
+ if (!groups.has(verb.group)) {
42
+ groups.set(verb.group, {
43
+ description: groupDescriptions.get(verb.group),
44
+ rows: [],
45
+ });
46
+ }
47
+ const args = formatArgs(verb.args);
48
+ groups.get(verb.group).rows.push({
49
+ command: `${verb.command}${args ? ` ${args}` : ""}`,
50
+ description: verb.description,
51
+ flags: formatFlags(verb.options),
52
+ });
122
53
  }
123
54
  const lines = [
124
55
  "",
125
56
  "_Generated from the CLI by `npm run gen:commands` - do not hand-edit between the markers. Run `graphit <cmd> --help` for exact flag values and descriptions._",
126
57
  "",
127
58
  ];
128
- for (const name of orderGroups(groups.keys())) {
59
+ for (const name of groups.keys()) {
129
60
  const { description, rows } = groups.get(name);
130
61
  lines.push(`**${name}**${description ? ` - ${esc(description)}` : ""}`);
131
62
  for (const row of rows) {