dsh-gitnexus-plus 0.0.0-stage → 0.1.2

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 (90) hide show
  1. package/LICENSE +38 -0
  2. package/README.md +631 -2
  3. package/cordis.patch.yml +18 -0
  4. package/lib/bin/gitnexus-coverage.d.ts +14 -0
  5. package/lib/bin/gitnexus-coverage.d.ts.map +1 -0
  6. package/lib/bin/gitnexus-coverage.js +171 -0
  7. package/lib/bin/gitnexus-coverage.js.map +1 -0
  8. package/lib/bin/search-helper.d.ts +22 -0
  9. package/lib/bin/search-helper.d.ts.map +1 -0
  10. package/lib/bin/search-helper.js +57 -0
  11. package/lib/bin/search-helper.js.map +1 -0
  12. package/lib/client.js +2131 -0
  13. package/lib/gitnexus/analyze.d.ts +173 -0
  14. package/lib/gitnexus/analyze.d.ts.map +1 -0
  15. package/lib/gitnexus/analyze.js +308 -0
  16. package/lib/gitnexus/analyze.js.map +1 -0
  17. package/lib/gitnexus/commands.d.ts +129 -0
  18. package/lib/gitnexus/commands.d.ts.map +1 -0
  19. package/lib/gitnexus/commands.js +185 -0
  20. package/lib/gitnexus/commands.js.map +1 -0
  21. package/lib/gitnexus/coverage.d.ts +340 -0
  22. package/lib/gitnexus/coverage.d.ts.map +1 -0
  23. package/lib/gitnexus/coverage.js +554 -0
  24. package/lib/gitnexus/coverage.js.map +1 -0
  25. package/lib/gitnexus/format.d.ts +16 -0
  26. package/lib/gitnexus/format.d.ts.map +1 -0
  27. package/lib/gitnexus/format.js +178 -0
  28. package/lib/gitnexus/format.js.map +1 -0
  29. package/lib/gitnexus/git-activity.d.ts +136 -0
  30. package/lib/gitnexus/git-activity.d.ts.map +1 -0
  31. package/lib/gitnexus/git-activity.js +345 -0
  32. package/lib/gitnexus/git-activity.js.map +1 -0
  33. package/lib/gitnexus/git.d.ts +70 -0
  34. package/lib/gitnexus/git.d.ts.map +1 -0
  35. package/lib/gitnexus/git.js +121 -0
  36. package/lib/gitnexus/git.js.map +1 -0
  37. package/lib/gitnexus/index-meta.d.ts +160 -0
  38. package/lib/gitnexus/index-meta.d.ts.map +1 -0
  39. package/lib/gitnexus/index-meta.js +176 -0
  40. package/lib/gitnexus/index-meta.js.map +1 -0
  41. package/lib/gitnexus/install.d.ts +97 -0
  42. package/lib/gitnexus/install.d.ts.map +1 -0
  43. package/lib/gitnexus/install.js +228 -0
  44. package/lib/gitnexus/install.js.map +1 -0
  45. package/lib/gitnexus/languages.d.ts +80 -0
  46. package/lib/gitnexus/languages.d.ts.map +1 -0
  47. package/lib/gitnexus/languages.js +297 -0
  48. package/lib/gitnexus/languages.js.map +1 -0
  49. package/lib/gitnexus/root.d.ts +74 -0
  50. package/lib/gitnexus/root.d.ts.map +1 -0
  51. package/lib/gitnexus/root.js +168 -0
  52. package/lib/gitnexus/root.js.map +1 -0
  53. package/lib/gitnexus/scan.d.ts +68 -0
  54. package/lib/gitnexus/scan.d.ts.map +1 -0
  55. package/lib/gitnexus/scan.js +249 -0
  56. package/lib/gitnexus/scan.js.map +1 -0
  57. package/lib/gitnexus/search.d.ts +167 -0
  58. package/lib/gitnexus/search.d.ts.map +1 -0
  59. package/lib/gitnexus/search.js +517 -0
  60. package/lib/gitnexus/search.js.map +1 -0
  61. package/lib/gitnexus/toolchain.d.ts +136 -0
  62. package/lib/gitnexus/toolchain.d.ts.map +1 -0
  63. package/lib/gitnexus/toolchain.js +251 -0
  64. package/lib/gitnexus/toolchain.js.map +1 -0
  65. package/lib/index.d.ts +98 -0
  66. package/lib/index.d.ts.map +1 -0
  67. package/lib/index.js +252 -0
  68. package/lib/index.js.map +1 -0
  69. package/lib/routes/analyze.d.ts +41 -0
  70. package/lib/routes/analyze.d.ts.map +1 -0
  71. package/lib/routes/analyze.js +75 -0
  72. package/lib/routes/analyze.js.map +1 -0
  73. package/lib/routes/coverage.d.ts +52 -0
  74. package/lib/routes/coverage.d.ts.map +1 -0
  75. package/lib/routes/coverage.js +81 -0
  76. package/lib/routes/coverage.js.map +1 -0
  77. package/lib/routes/guard.d.ts +92 -0
  78. package/lib/routes/guard.d.ts.map +1 -0
  79. package/lib/routes/guard.js +187 -0
  80. package/lib/routes/guard.js.map +1 -0
  81. package/lib/routes/install.d.ts +27 -0
  82. package/lib/routes/install.d.ts.map +1 -0
  83. package/lib/routes/install.js +46 -0
  84. package/lib/routes/install.js.map +1 -0
  85. package/lib/routes/search.d.ts +27 -0
  86. package/lib/routes/search.d.ts.map +1 -0
  87. package/lib/routes/search.js +77 -0
  88. package/lib/routes/search.js.map +1 -0
  89. package/package.json +87 -4
  90. package/scripts/link-runtime.mjs +146 -0
@@ -0,0 +1,173 @@
1
+ /**
2
+ * Run `gitnexus analyze --index-only` through the host's shell executor.
3
+ *
4
+ * WHY THIS REPLACED HANDING OVER A COMMAND
5
+ * ----------------------------------------
6
+ * The plugin used to print a command and let the operator paste it. That was
7
+ * justified as "a plugin that spawns processes bypasses the permission system", and
8
+ * the justification was wrong — not in spirit, but in what it assumed permission
9
+ * meant here.
10
+ *
11
+ * DSH's approval service governs **sandbox escalation**: widening the scope a session
12
+ * was granted. Its audit pair must be turn-enclosed, and `ApprovalService.request()`
13
+ * throws outside an open turn. A panel route has no turn, so it cannot ask — but it
14
+ * also does not need to, because this command does not escalate anything. It writes
15
+ * `.gitnexus/` **inside the workspace**, which is already within the granted scope.
16
+ *
17
+ * The confinement is not something a caller opts into. It is what `ctx.shell` *is* in
18
+ * a given deployment: `dsh-pwsh-local` exposes the argv seam that `dsh-pwsh-sandbox`
19
+ * wraps through `ctx.sandbox.confine`. So routing through the executor gives this
20
+ * plugin exactly the isolation the agent's own shell tool gets.
21
+ *
22
+ * THE ONE RULE THAT MATTERS
23
+ * -------------------------
24
+ * `sandboxPolicy` is never set here, and must never be. Passing it would be this
25
+ * plugin choosing its own confinement — the escalation the approval service exists to
26
+ * gate, requested through a door that cannot ask. Leaving it undefined takes the
27
+ * deployment's decision. A denial is then reported as a denial, not retried wider:
28
+ * the same rule the subagent prompt states, for the same reason.
29
+ */
30
+ import { type ToolchainOptions } from './toolchain.js';
31
+ /** Where a run got to. `unavailable` is decided before anything is spawned. */
32
+ export type AnalyzeState =
33
+ /** No `gitnexus` executable was found — there is nothing to run. */
34
+ 'unavailable' | 'running' | 'done'
35
+ /** The command ran and exited non-zero. */
36
+ | 'failed' | 'timed-out' | 'cancelled'
37
+ /** The sandbox refused a write. Reported, never retried wider. */
38
+ | 'denied';
39
+ export interface AnalyzeStatus {
40
+ repoPath: string;
41
+ state: AnalyzeState;
42
+ /** Whether this run was a full rebuild (`--force`). */
43
+ force: boolean;
44
+ /** The command as run, so a failure can be reproduced by hand. */
45
+ command: string;
46
+ startedAt: number;
47
+ finishedAt: number | null;
48
+ exitCode: number | null;
49
+ /** A short operator-facing explanation, or null when the state speaks for itself. */
50
+ message: string | null;
51
+ /** The tail of combined output, capped. */
52
+ output: string;
53
+ }
54
+ /**
55
+ * The slice of `ctx.shell` this module uses.
56
+ *
57
+ * Structural rather than an import: the plugin declares no runtime dependency on the
58
+ * DSH packages, and a test can supply a stub without a shell on the machine.
59
+ */
60
+ export interface ShellRequestLike {
61
+ command: string;
62
+ workdir?: string;
63
+ timeoutMs?: number;
64
+ onExpiry?: 'kill' | 'none';
65
+ stdoutMaxBytes?: number;
66
+ signal?: AbortSignal;
67
+ }
68
+ export interface ShellResultLike {
69
+ exitCode?: number | null;
70
+ signal?: string | null;
71
+ timedOut?: boolean;
72
+ aborted?: boolean;
73
+ stdout?: {
74
+ text?: string;
75
+ } | null;
76
+ stderr?: {
77
+ text?: string;
78
+ } | null;
79
+ }
80
+ export interface ShellExecutionLike {
81
+ result(): Promise<ShellResultLike>;
82
+ kill?(): boolean | Promise<boolean>;
83
+ }
84
+ export interface ShellLike {
85
+ /** Applies the executor's defaults and caps. `execute` only accepts a resolved spec. */
86
+ resolve(request: ShellRequestLike): unknown;
87
+ execute(spec: unknown): Promise<ShellExecutionLike>;
88
+ }
89
+ export interface AnalyzeRunnerOptions {
90
+ /**
91
+ * The host's shell executor, or `null` when the deployment has none.
92
+ *
93
+ * `null` is a real composition rather than a test convenience: `ctx.shell` is mounted
94
+ * per profile, and a headless one need not have it. A runner that reported "failed"
95
+ * for a missing service would send the operator looking for a GitNexus problem that
96
+ * is really a deployment shape.
97
+ */
98
+ shell: ShellLike | null;
99
+ /** Wall-clock ceiling for one run. Default 10 minutes — a full rebuild is minutes. */
100
+ timeoutMs?: number;
101
+ /** Captured-output ceiling handed to the executor. */
102
+ outputLimit?: number;
103
+ /** How much output this module keeps for the panel. */
104
+ tailLimit?: number;
105
+ toolchain?: ToolchainOptions;
106
+ /**
107
+ * Pass `GITNEXUS_MEMORY=off` and the heap override, exactly as the handed-over
108
+ * commands do. On by default: the CLI re-spawns itself over piped stdio without it,
109
+ * which a confined process denies.
110
+ */
111
+ memoryOff?: boolean;
112
+ heapMb?: number;
113
+ /**
114
+ * `true` forces `--skip-fts`. `false`/absent is **automatic**: the runner asks the
115
+ * index whether FTS exists and skips it when it does not. A static `false` was
116
+ * measured to fail with `spawn EPERM` on an index without FTS (the extension tries
117
+ * to install out-of-process), while a static `true` forces a full rebuild on one
118
+ * that has it.
119
+ */
120
+ skipFts?: boolean;
121
+ /** Override the redirect of the global registry write. */
122
+ gitnexusHome?: string | null;
123
+ now?: () => number;
124
+ }
125
+ export interface AnalyzeRunner {
126
+ /**
127
+ * Begin a run, or report the one already in flight for this repository.
128
+ *
129
+ * `force` selects a full rebuild. It is a request, not a second run: two analyze
130
+ * processes on one store contend, so a force request while an incremental run is
131
+ * in flight returns that run.
132
+ */
133
+ start(repoPath: string, options?: {
134
+ force?: boolean;
135
+ }): AnalyzeStatus;
136
+ status(repoPath: string): AnalyzeStatus | null;
137
+ cancel(repoPath: string): boolean;
138
+ /** Abort every in-flight run. Called on plugin teardown. */
139
+ dispose(): void;
140
+ }
141
+ /**
142
+ * Build the command line, as the shell will read it.
143
+ *
144
+ * TWO THINGS HERE WERE WRONG ON THE FIRST ATTEMPT, AND TOGETHER THEY MADE THE BUTTON
145
+ * DO NOTHING VISIBLE — a run started, finished, and the index never changed.
146
+ *
147
+ * 1. PowerShell needs the call operator before a quoted path.
148
+ * `'C:\...\gitnexus.cmd' analyze` is not a command: the single-quoted string is a
149
+ * string *literal*, and the parser dies on the next token. Measured:
150
+ * "表达式或语句中包含意外的标记" for the whole statement. The paste-able text in
151
+ * `commands.ts` never hit this because it names a bare `gitnexus` and lets PATH
152
+ * resolve it — a difference that exists only because this path is absolute.
153
+ *
154
+ * 2. The environment prefix is not optional. `GITNEXUS_MEMORY=off` stops the CLI
155
+ * re-spawning itself over piped stdio, which a confined process denies. Dropping it
156
+ * because the command "looked simpler" reintroduces exactly the failure the
157
+ * maintenance text was written to avoid.
158
+ *
159
+ * Exported so the shape can be asserted without a shell.
160
+ */
161
+ export declare function analyzeCommandLine(cliPath: string, repoPath: string, options?: {
162
+ flavor?: 'win32' | 'posix';
163
+ memoryOff?: boolean;
164
+ heapMb?: number;
165
+ viaNode?: boolean;
166
+ /** Full rebuild. This is the destructive escape hatch; the button must say so. */
167
+ force?: boolean;
168
+ /** Must match `maintenanceCommands`; see the note on the runner option. */
169
+ skipFts?: boolean;
170
+ gitnexusHome?: string | null;
171
+ }): string;
172
+ export declare function createAnalyzeRunner(options: AnalyzeRunnerOptions): AnalyzeRunner;
173
+ //# sourceMappingURL=analyze.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyze.d.ts","sourceRoot":"","sources":["../../src/gitnexus/analyze.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,308 @@
1
+ /**
2
+ * Run `gitnexus analyze --index-only` through the host's shell executor.
3
+ *
4
+ * WHY THIS REPLACED HANDING OVER A COMMAND
5
+ * ----------------------------------------
6
+ * The plugin used to print a command and let the operator paste it. That was
7
+ * justified as "a plugin that spawns processes bypasses the permission system", and
8
+ * the justification was wrong — not in spirit, but in what it assumed permission
9
+ * meant here.
10
+ *
11
+ * DSH's approval service governs **sandbox escalation**: widening the scope a session
12
+ * was granted. Its audit pair must be turn-enclosed, and `ApprovalService.request()`
13
+ * throws outside an open turn. A panel route has no turn, so it cannot ask — but it
14
+ * also does not need to, because this command does not escalate anything. It writes
15
+ * `.gitnexus/` **inside the workspace**, which is already within the granted scope.
16
+ *
17
+ * The confinement is not something a caller opts into. It is what `ctx.shell` *is* in
18
+ * a given deployment: `dsh-pwsh-local` exposes the argv seam that `dsh-pwsh-sandbox`
19
+ * wraps through `ctx.sandbox.confine`. So routing through the executor gives this
20
+ * plugin exactly the isolation the agent's own shell tool gets.
21
+ *
22
+ * THE ONE RULE THAT MATTERS
23
+ * -------------------------
24
+ * `sandboxPolicy` is never set here, and must never be. Passing it would be this
25
+ * plugin choosing its own confinement — the escalation the approval service exists to
26
+ * gate, requested through a door that cannot ask. Leaving it undefined takes the
27
+ * deployment's decision. A denial is then reported as a denial, not retried wider:
28
+ * the same rule the subagent prompt states, for the same reason.
29
+ */
30
+ import path from 'node:path';
31
+ import { defaultGitnexusHome, effectiveSkipFts } from './commands.js';
32
+ import { findGitnexusCli } from './toolchain.js';
33
+ const DEFAULT_TIMEOUT_MS = 10 * 60 * 1000;
34
+ const DEFAULT_OUTPUT_LIMIT = 256 * 1024;
35
+ const DEFAULT_TAIL_LIMIT = 4 * 1024;
36
+ /**
37
+ * Quote one argument for the shell the executor will spawn.
38
+ *
39
+ * Windows and POSIX disagree, and `commands.ts` already carries the same pair — kept
40
+ * separate here because these two modules are allowed to have different lifetimes:
41
+ * one produces text for a human to paste, the other produces argv for a spawn, and
42
+ * collapsing them would make a change to the paste-able text silently alter what
43
+ * actually runs.
44
+ */
45
+ function quoteFor(shell, value) {
46
+ return shell === 'win32' ? `'${value.replace(/'/g, "''")}'` : `'${value.replace(/'/g, "'\\''")}'`;
47
+ }
48
+ /**
49
+ * Build the command line, as the shell will read it.
50
+ *
51
+ * TWO THINGS HERE WERE WRONG ON THE FIRST ATTEMPT, AND TOGETHER THEY MADE THE BUTTON
52
+ * DO NOTHING VISIBLE — a run started, finished, and the index never changed.
53
+ *
54
+ * 1. PowerShell needs the call operator before a quoted path.
55
+ * `'C:\...\gitnexus.cmd' analyze` is not a command: the single-quoted string is a
56
+ * string *literal*, and the parser dies on the next token. Measured:
57
+ * "表达式或语句中包含意外的标记" for the whole statement. The paste-able text in
58
+ * `commands.ts` never hit this because it names a bare `gitnexus` and lets PATH
59
+ * resolve it — a difference that exists only because this path is absolute.
60
+ *
61
+ * 2. The environment prefix is not optional. `GITNEXUS_MEMORY=off` stops the CLI
62
+ * re-spawning itself over piped stdio, which a confined process denies. Dropping it
63
+ * because the command "looked simpler" reintroduces exactly the failure the
64
+ * maintenance text was written to avoid.
65
+ *
66
+ * Exported so the shape can be asserted without a shell.
67
+ */
68
+ export function analyzeCommandLine(cliPath, repoPath, options = {}) {
69
+ const flavor = options.flavor ?? (process.platform === 'win32' ? 'win32' : 'posix');
70
+ const memoryOff = options.memoryOff !== false;
71
+ const heapMb = options.heapMb ?? 8192;
72
+ // A CLI taken from the declared dependency is the package's `dist/cli/index.js`, so it
73
+ // is spawned through `node`. This is the better shape when available: no shim, no
74
+ // `.cmd`/`.ps1` selection, and therefore no reliance on the shell's call operator.
75
+ const viaNode = options.viaNode === true;
76
+ const args = `analyze --index-only` +
77
+ `${options.skipFts === true ? ' --skip-fts' : ''}` +
78
+ `${options.force === true ? ' --force' : ''} ` +
79
+ quoteFor(flavor, repoPath);
80
+ const head = viaNode ? `node ${quoteFor(flavor, cliPath)}` : quoteFor(flavor, cliPath);
81
+ // On Windows a `.cmd` or `.ps1` reached by absolute path needs `&`. POSIX needs none,
82
+ // because an absolute path in command position is already a command.
83
+ const invocation = viaNode || flavor !== 'win32' ? `${head} ${args}` : `& ${head} ${args}`;
84
+ const heap = quoteFor(flavor, `--max-old-space-size=${heapMb}`);
85
+ const home = typeof options.gitnexusHome === 'string' && options.gitnexusHome !== ''
86
+ ? quoteFor(flavor, options.gitnexusHome)
87
+ : null;
88
+ if (flavor === 'win32') {
89
+ const prefix = memoryOff ? `$env:GITNEXUS_MEMORY='off'; ` : '';
90
+ const homePrefix = home === null ? '' : `$env:GITNEXUS_HOME=${home}; `;
91
+ return `${prefix}${homePrefix}$env:NODE_OPTIONS=${heap}; ${invocation}`;
92
+ }
93
+ const prefix = memoryOff ? `GITNEXUS_MEMORY='off' ` : '';
94
+ const homePrefix = home === null ? '' : `GITNEXUS_HOME=${home} `;
95
+ return `${prefix}${homePrefix}NODE_OPTIONS=${heap} ${invocation}`;
96
+ }
97
+ /**
98
+ * A sandbox refusal, recognised from the marker DSH writes into the output.
99
+ *
100
+ * Tested before the generic non-zero path because a denial is not a failure of
101
+ * GitNexus — retrying it, or reporting it as "analyze failed", both mislead.
102
+ */
103
+ function looksDenied(text) {
104
+ return /\[sandbox:[^\]]*denied/i.test(text) || /access is denied|permission denied|EPERM/i.test(text);
105
+ }
106
+ /** Keep the last `limit` characters, on a line boundary when one is available. */
107
+ function tail(text, limit) {
108
+ if (text.length <= limit)
109
+ return text;
110
+ const cut = text.slice(text.length - limit);
111
+ const firstBreak = cut.indexOf('\n');
112
+ return firstBreak === -1 ? cut : cut.slice(firstBreak + 1);
113
+ }
114
+ export function createAnalyzeRunner(options) {
115
+ const { shell } = options;
116
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
117
+ const outputLimit = options.outputLimit ?? DEFAULT_OUTPUT_LIMIT;
118
+ const tailLimit = options.tailLimit ?? DEFAULT_TAIL_LIMIT;
119
+ const now = options.now ?? (() => Date.now());
120
+ const shellFlavor = process.platform === 'win32' ? 'win32' : 'posix';
121
+ const runs = new Map();
122
+ const inFlight = new Map();
123
+ /** The shape a status starts from, before any run has touched it. */
124
+ const BLANK = {
125
+ state: 'failed',
126
+ force: false,
127
+ command: '',
128
+ startedAt: 0,
129
+ finishedAt: null,
130
+ exitCode: null,
131
+ message: null,
132
+ output: '',
133
+ };
134
+ const finish = (repoPath, patch) => {
135
+ const next = {
136
+ repoPath,
137
+ ...BLANK,
138
+ ...runs.get(repoPath),
139
+ ...patch,
140
+ // Set last and unconditionally: every path that reaches `finish` is finishing, so
141
+ // a caller passing `finishedAt: null` is asking for "not finished", not for a
142
+ // silent no-op. The spread above cannot express that.
143
+ finishedAt: patch.finishedAt ?? now(),
144
+ };
145
+ runs.set(repoPath, next);
146
+ inFlight.delete(repoPath);
147
+ return next;
148
+ };
149
+ const start = (repoPath, request = {}) => {
150
+ const key = path.resolve(repoPath);
151
+ // Named `request`, not `options`: the runner's own configuration is the outer
152
+ // `options`, and shadowing it silently dropped `toolchain`, `memoryOff` and
153
+ // `heapMb` from every run built after the force argument was added.
154
+ const force = request.force === true;
155
+ // Both decisions are per repository, not per plugin: the skip-fts policy depends
156
+ // on the index's own FTS capability, and the registry redirect belongs beside the
157
+ // index so it stays inside the workspace the host was granted.
158
+ const skipFts = effectiveSkipFts(key, options.skipFts === true);
159
+ const gitnexusHome = options.gitnexusHome ?? defaultGitnexusHome(key);
160
+ // One run per repository. Two concurrent `analyze` processes would contend for the
161
+ // same store, and the second would report a failure that says nothing about the
162
+ // repository — so a second request reports the first rather than starting one.
163
+ const running = runs.get(key);
164
+ if (running !== undefined && running.state === 'running')
165
+ return running;
166
+ if (shell === null) {
167
+ return finish(key, {
168
+ state: 'unavailable',
169
+ force,
170
+ command: `gitnexus analyze --index-only${force ? ' --force' : ''}`,
171
+ startedAt: now(),
172
+ output: '',
173
+ message: '当前部署没有提供 shell 执行器(ctx.shell 未挂载),无法运行命令。' +
174
+ '请复制下面的命令在终端中执行。',
175
+ });
176
+ }
177
+ // `moduleUrl` is what lets the probe find the `gitnexus` that npm installed beside
178
+ // this plugin. Omitting it would make the dependency branch unreachable and silently
179
+ // fall back to PATH forever — the dependency would be installed and never used.
180
+ const cli = findGitnexusCli({ moduleUrl: import.meta.url, ...(options.toolchain ?? {}) });
181
+ if (!cli.present) {
182
+ return finish(key, {
183
+ state: 'unavailable',
184
+ force,
185
+ command: `gitnexus analyze --index-only${force ? ' --force' : ''}`,
186
+ startedAt: now(),
187
+ output: '',
188
+ message: '本机没有找到 gitnexus 命令,无法执行。安装后重试:`npm i -g gitnexus`。' +
189
+ '扫描与覆盖数据仍然可用 —— 那部分不需要 CLI。',
190
+ });
191
+ }
192
+ // `--index-only` is not negotiable here: without it `analyze` rewrites AGENTS.md,
193
+ // CLAUDE.md and the agent skill files inside the repository, which is a much larger
194
+ // change than "refresh the index" and not what the button says it does.
195
+ const command = analyzeCommandLine(cli.cliPath, key, {
196
+ flavor: shellFlavor,
197
+ memoryOff: options.memoryOff,
198
+ heapMb: options.heapMb,
199
+ viaNode: cli.viaNode,
200
+ force,
201
+ skipFts,
202
+ gitnexusHome,
203
+ });
204
+ const controller = new AbortController();
205
+ inFlight.set(key, controller);
206
+ const started = {
207
+ repoPath: key,
208
+ state: 'running',
209
+ force,
210
+ command,
211
+ startedAt: now(),
212
+ finishedAt: null,
213
+ exitCode: null,
214
+ message: null,
215
+ output: '',
216
+ };
217
+ runs.set(key, started);
218
+ const spec = shell.resolve({
219
+ command,
220
+ workdir: key,
221
+ timeoutMs,
222
+ onExpiry: 'kill',
223
+ stdoutMaxBytes: outputLimit,
224
+ signal: controller.signal,
225
+ // `sandboxPolicy` is deliberately absent. See the module comment.
226
+ });
227
+ void (async () => {
228
+ let execution;
229
+ try {
230
+ execution = await shell.execute(spec);
231
+ }
232
+ catch (cause) {
233
+ // `execute` rejects only for infrastructure failures — an unusable workdir, no
234
+ // shell. A cancellation rejects here too, before a handle exists.
235
+ const aborted = controller.signal.aborted;
236
+ finish(key, {
237
+ state: aborted ? 'cancelled' : 'failed',
238
+ message: aborted ? '已取消。' : `无法启动命令:${String(cause?.message ?? cause)}`,
239
+ });
240
+ return;
241
+ }
242
+ let result;
243
+ try {
244
+ result = await execution.result();
245
+ }
246
+ catch (cause) {
247
+ finish(key, { state: 'failed', message: `执行失败:${String(cause?.message ?? cause)}` });
248
+ return;
249
+ }
250
+ const combined = `${result.stdout?.text ?? ''}${result.stderr?.text ?? ''}`;
251
+ const body = tail(combined, tailLimit);
252
+ const exitCode = typeof result.exitCode === 'number' ? result.exitCode : null;
253
+ // Order matters. A denial can also be a non-zero exit, and reporting it as a
254
+ // generic failure sends the operator looking for a GitNexus bug that is not
255
+ // there. Cancellation is checked before timeout because a kill from `cancel()`
256
+ // and a kill from the deadline both surface as a killed process.
257
+ if (controller.signal.aborted) {
258
+ finish(key, { state: 'cancelled', exitCode, output: body, message: '已取消。' });
259
+ }
260
+ else if (result.timedOut === true) {
261
+ finish(key, {
262
+ state: 'timed-out',
263
+ exitCode,
264
+ output: body,
265
+ message: `超过 ${Math.round(timeoutMs / 1000)} 秒上限已终止。大仓库的全量重建可能需要更长时间。`,
266
+ });
267
+ }
268
+ else if (looksDenied(combined)) {
269
+ finish(key, {
270
+ state: 'denied',
271
+ exitCode,
272
+ output: body,
273
+ message: '沙箱拒绝了这次写入。本插件不会申请扩权 —— 需要在终端里手动执行这条命令。',
274
+ });
275
+ }
276
+ else if (exitCode === 0) {
277
+ finish(key, { state: 'done', exitCode, output: body, message: null });
278
+ }
279
+ else {
280
+ finish(key, {
281
+ state: 'failed',
282
+ exitCode,
283
+ output: body,
284
+ message: `gitnexus 以退出码 ${exitCode ?? '未知'} 结束。`,
285
+ });
286
+ }
287
+ })();
288
+ return started;
289
+ };
290
+ return {
291
+ start,
292
+ status: (repoPath) => runs.get(path.resolve(repoPath)) ?? null,
293
+ cancel: (repoPath) => {
294
+ const key = path.resolve(repoPath);
295
+ const controller = inFlight.get(key);
296
+ if (controller === undefined)
297
+ return false;
298
+ controller.abort();
299
+ return true;
300
+ },
301
+ dispose: () => {
302
+ for (const controller of inFlight.values())
303
+ controller.abort();
304
+ inFlight.clear();
305
+ },
306
+ };
307
+ }
308
+ //# sourceMappingURL=analyze.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyze.js","sourceRoot":"","sources":["../../src/gitnexus/analyze.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,129 @@
1
+ /**
2
+ * The maintenance commands this plugin hands to the operator.
3
+ *
4
+ * WHY THE TEXT STILL EXISTS AFTER THE BUTTONS
5
+ * -------------------------------------------
6
+ * A first revision spawned `gitnexus analyze` directly from the plugin and exposed
7
+ * it as a button. That was wrong: a plugin that spawns processes bypasses the host's
8
+ * permission system. The correction was not "never run it" but "run it through
9
+ * `ctx.shell`", which is what the analyze runner does. The exact command is still the
10
+ * deliverable here because it is what the confirmation shows, what a failed run can
11
+ * be reproduced with, and what an operator can paste when the deployment has no
12
+ * shell executor at all. The two must not drift: the runner builds its argv from the
13
+ * same decisions this module documents.
14
+ *
15
+ * WHAT MAKES THE COMMANDS NON-OBVIOUS
16
+ * -----------------------------------
17
+ * A bare `gitnexus analyze` does **not** work in every deployment, and the failures
18
+ * are silent-ish. Measured on this machine, under a confined DSH process:
19
+ *
20
+ * - `ensureHeap` re-spawns the CLI with a larger heap over piped stdio, which a
21
+ * confined sandbox denies (`spawn EPERM`). `GITNEXUS_MEMORY=off` suppresses the
22
+ * respawn; `NODE_OPTIONS` supplies the heap it was trying to obtain.
23
+ * - The FTS extension installs itself out-of-process and fails the same way.
24
+ * `--skip-fts` builds the index without it.
25
+ * - The global registry write (`~/.gitnexus/registry.json`) is outside the
26
+ * workspace and may be denied. `GITNEXUS_HOME` redirects it.
27
+ *
28
+ * `--index-only` on both commands is deliberate and not a sandbox workaround:
29
+ * the default `analyze` also rewrites AGENTS.md, CLAUDE.md and agent skill files
30
+ * inside the repository. A maintenance button must not edit the working tree.
31
+ */
32
+ /**
33
+ * Where this plugin keeps GitNexus's global registry for one repository.
34
+ *
35
+ * `GITNEXUS_HOME` is normally `~/.gitnexus`, outside a confined host's writable
36
+ * scope. Pointing it at a directory inside the project's own `.gitnexus/` keeps the
37
+ * registry write next to the index and inside the workspace — which is the scope the
38
+ * operator already granted. The analysis itself still writes `.gitnexus/lbug`; this
39
+ * only moves the global registry side effect.
40
+ */
41
+ export declare function defaultGitnexusHome(repoPath: string): string;
42
+ /**
43
+ * Whether this index says content-level FTS is available.
44
+ *
45
+ * A missing manifest, a legacy manifest, or a status other than `available` all mean
46
+ * "do not attempt FTS". The read is synchronous because both the maintenance-command
47
+ * builder and the analyze runner are synchronous; the manifest is a few kilobytes.
48
+ */
49
+ export declare function ftsAvailable(repoPath: string): boolean;
50
+ /**
51
+ * The effective `--skip-fts` decision for one repository.
52
+ *
53
+ * `false` (the default) means **automatic**: skip FTS when the index does not have
54
+ * it, so a refresh in a confined process does not die trying to install an extension
55
+ * out-of-process. `true` forces the flag. There is deliberately no "force FTS on":
56
+ * that is a repair action (`gitnexus analyze --repair-fts`), not an incremental
57
+ * refresh, and hiding a repair behind a config boolean would mislead.
58
+ *
59
+ * The alternative — a static default — was measured wrong in both directions: `true`
60
+ * forces a 21x full rebuild on an index that already has FTS, while `false` makes the
61
+ * refresh fail with `spawn EPERM` on an index that does not.
62
+ */
63
+ export declare function effectiveSkipFts(repoPath: string, configured: boolean): boolean;
64
+ export type MaintenanceMode = 'init' | 'full';
65
+ export interface MaintenanceCommand {
66
+ mode: MaintenanceMode;
67
+ label: string;
68
+ /** When the operator should reach for this one. */
69
+ when: string;
70
+ /** The command alone, for reference. */
71
+ argv: string;
72
+ /** The command with the environment it needs, ready to paste. */
73
+ shell: string;
74
+ /** Whether it discards existing index work. */
75
+ destructive: boolean;
76
+ caveat: string | null;
77
+ }
78
+ export interface MaintenanceOptions {
79
+ platform?: NodeJS.Platform;
80
+ /**
81
+ * Add `--skip-fts`. Off by default, and that default is load-bearing — see the
82
+ * measurement in {@link maintenanceCommands}.
83
+ */
84
+ skipFts?: boolean;
85
+ memoryOff?: boolean;
86
+ gitnexusHome?: string | null;
87
+ }
88
+ /** The resolved form of {@link MaintenanceOptions}, after defaults are applied. */
89
+ interface ResolvedOptions {
90
+ platform: NodeJS.Platform;
91
+ skipFts: boolean;
92
+ memoryOff: boolean;
93
+ gitnexusHome: string | null;
94
+ }
95
+ /**
96
+ * Build the two commands for a repository.
97
+ *
98
+ * Pure, because the exact text is the deliverable — a reviewer should be able to
99
+ * assert on it without spawning anything.
100
+ *
101
+ * WHY `--skip-fts` IS NOT A DEFAULT
102
+ * ---------------------------------
103
+ * It was one, on the reasoning that the FTS extension installs out-of-process and a
104
+ * confined process denies that (`spawn EPERM`). Measured on a 39-file repository,
105
+ * that reasoning is wrong in the case that matters:
106
+ *
107
+ * index has FTS + `--skip-fts` 179.22 s full DB wipe + bulk COPY
108
+ * index has FTS, no flag 8.57 s FTS fails to load, degrades cleanly
109
+ * index has no FTS + `--skip-fts` 8.25 s changed=0, 39 rows preserved
110
+ * same, warm 6.56 s
111
+ *
112
+ * Disabling a search index the store already has forces GitNexus to rebuild the
113
+ * whole graph store, which is the exact opposite of incremental — a 21x penalty on
114
+ * the one transition an operator is most likely to hit. Letting FTS fail on its own
115
+ * costs nothing: it prints a warning and indexes anyway.
116
+ *
117
+ * The flag remains available for the case it was written for, where the extension
118
+ * file is *absent* and the install itself is what fails.
119
+ */
120
+ export declare function maintenanceCommands(repoPath: string, options?: MaintenanceOptions): MaintenanceCommand[];
121
+ /** The options {@link commandRationale} needs — a subset of the command options. */
122
+ export type RationaleOptions = Pick<ResolvedOptions, 'skipFts' | 'memoryOff' | 'gitnexusHome'>;
123
+ /**
124
+ * A one-line explanation of why the environment prefix is there at all, for the
125
+ * panel to show under the commands rather than leaving them looking arbitrary.
126
+ */
127
+ export declare function commandRationale(options: RationaleOptions): string[];
128
+ export {};
129
+ //# sourceMappingURL=commands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/gitnexus/commands.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAQH;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAetD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,GAAG,OAAO,CAE/E;AAED,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,MAAM,CAAC;AAE9C,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,eAAe,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,IAAI,EAAE,MAAM,CAAC;IACb,wCAAwC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAC;IACd,+CAA+C;IAC/C,WAAW,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IAC3B;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,mFAAmF;AACnF,UAAU,eAAe;IACvB,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;IACnB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,kBAAuB,GAAG,kBAAkB,EAAE,CA0D5G;AAED,oFAAoF;AACpF,MAAM,MAAM,gBAAgB,GAAG,IAAI,CAAC,eAAe,EAAE,SAAS,GAAG,WAAW,GAAG,cAAc,CAAC,CAAC;AAE/F;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,EAAE,CAcpE"}