pi-usereq 0.83.0 → 0.84.0
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/README.md +1 -1
- package/package.json +1 -1
- package/src/core/req-references-command.ts +51 -11
- package/src/index.ts +19 -4
package/README.md
CHANGED
package/package.json
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { spawnSync, type SpawnSyncReturns } from "node:child_process";
|
|
8
|
+
import fs from "node:fs";
|
|
8
9
|
import path from "node:path";
|
|
9
10
|
import type { UseReqConfig } from "./config.js";
|
|
10
11
|
import { ReqError } from "./errors.js";
|
|
@@ -34,6 +35,22 @@ export interface ReqReferencesCommandPlan {
|
|
|
34
35
|
commitMessage: string;
|
|
35
36
|
}
|
|
36
37
|
|
|
38
|
+
/**
|
|
39
|
+
* @brief Describes the per-operation outcome facts of one completed `req-references` run.
|
|
40
|
+
* @details Reports whether the regenerated `REFERENCES.md` content differs from the pre-run file and whether the guarded commit created a commit or was skipped, so the extension command handler can emit notifications that conform to the operations actually performed regardless of the update and commit outcomes. The interface is compile-time only and introduces no runtime cost.
|
|
41
|
+
* @satisfies REQ-436
|
|
42
|
+
*/
|
|
43
|
+
export interface ReqReferencesCommandExecutionResult {
|
|
44
|
+
/** Absolute path of the regenerated `REFERENCES.md` document. */
|
|
45
|
+
referencesPath: string;
|
|
46
|
+
/** `true` when the regenerated content differs from the pre-run file content or the file was absent. */
|
|
47
|
+
referencesChanged: boolean;
|
|
48
|
+
/** Fixed commit message that a created commit carries. */
|
|
49
|
+
commitMessage: string;
|
|
50
|
+
/** `true` when the guarded commit created a commit; `false` when it was skipped because content matched `HEAD`. */
|
|
51
|
+
commitCreated: boolean;
|
|
52
|
+
}
|
|
53
|
+
|
|
37
54
|
/**
|
|
38
55
|
* @brief Executes one synchronous subprocess and captures UTF-8 output.
|
|
39
56
|
* @details Delegates to `spawnSync(...)`, preserves the supplied working directory, and returns the raw result so callers can interpret git exit status plus diagnostics deterministically. Runtime is dominated by external process execution. Side effects include subprocess creation.
|
|
@@ -135,17 +152,17 @@ function hasStagedChangesForPaths(gitRoot: string, targetPaths: string[]): boole
|
|
|
135
152
|
|
|
136
153
|
/**
|
|
137
154
|
* @brief Executes one guarded `git commit` invocation for the staged target paths.
|
|
138
|
-
* @details Runs a staged-changes precheck against the cached index and returns without creating a commit when no staged difference exists for the target paths, preventing empty-commit failures such as `nothing to commit, working tree clean`. When a staged difference exists, delegates to `git commit -m <commitMessage
|
|
155
|
+
* @details Runs a staged-changes precheck against the cached index and returns `false` without creating a commit when no staged difference exists for the target paths, preventing empty-commit failures such as `nothing to commit, working tree clean`. When a staged difference exists, delegates to `git commit -m <commitMessage>`, converts any non-zero result into a deterministic `ReqError`, and returns `true`. Runtime is dominated by up to two git subprocesses. Side effects include subprocess creation and conditional commit creation.
|
|
139
156
|
* @param[in] gitRoot {string} Absolute git root path.
|
|
140
157
|
* @param[in] targetPaths {string[]} Git-add target paths inspected by the staged-changes precheck.
|
|
141
158
|
* @param[in] commitMessage {string} Commit message used when a staged difference exists.
|
|
142
|
-
* @return {
|
|
159
|
+
* @return {boolean} `true` when the commit was created; `false` when it was skipped because the cached index held no staged difference for the target paths.
|
|
143
160
|
* @throws {ReqError} Throws when staged-difference inspection or commit creation fails.
|
|
144
161
|
* @satisfies REQ-357, REQ-358
|
|
145
162
|
*/
|
|
146
|
-
function runGuardedGitCommit(gitRoot: string, targetPaths: string[], commitMessage: string):
|
|
163
|
+
function runGuardedGitCommit(gitRoot: string, targetPaths: string[], commitMessage: string): boolean {
|
|
147
164
|
if (!hasStagedChangesForPaths(gitRoot, targetPaths)) {
|
|
148
|
-
return;
|
|
165
|
+
return false;
|
|
149
166
|
}
|
|
150
167
|
const commitResult = runCapture(["git", "commit", "-m", commitMessage], gitRoot);
|
|
151
168
|
if (commitResult.error || commitResult.status !== 0) {
|
|
@@ -155,6 +172,7 @@ function runGuardedGitCommit(gitRoot: string, targetPaths: string[], commitMessa
|
|
|
155
172
|
|| "unknown error";
|
|
156
173
|
throw new ReqError(`ERROR: git commit failed: ${diagnostic}`, 1);
|
|
157
174
|
}
|
|
175
|
+
return true;
|
|
158
176
|
}
|
|
159
177
|
|
|
160
178
|
/**
|
|
@@ -182,28 +200,50 @@ export function prepareReqReferencesCommandExecution(
|
|
|
182
200
|
}
|
|
183
201
|
|
|
184
202
|
/**
|
|
185
|
-
* @brief Executes the specialized `req-references` direct-write workflow.
|
|
186
|
-
* @details
|
|
203
|
+
* @brief Executes the specialized `req-references` direct-write workflow and reports per-operation outcome facts.
|
|
204
|
+
* @details Snapshots the pre-run `REFERENCES.md` content, regenerates the document through the same source-summary path used by the `references` tool, compares the regenerated content against the pre-run snapshot, stages only the target file, creates the fixed-message commit through the guarded commit helper whenever a staged difference exists, verifies that no residual git-status rows remain after ignored extension-owned debug artifacts are filtered out, and returns whether the references content changed and whether the commit was created or skipped so callers can emit outcome-conformant notifications. Runtime is dominated by summary generation plus two to four git subprocesses. Side effects include documentation writes, index mutation, conditional commit creation, and subprocess creation.
|
|
187
205
|
* @param[in] plan {ReqReferencesCommandPlan} Prepared direct-write execution plan.
|
|
188
206
|
* @param[in] config {UseReqConfig} Effective project configuration.
|
|
189
|
-
* @return {
|
|
190
|
-
* @throws {ReqError} Throws when reference generation, staging, guarded commit creation, or cleanliness verification fails.
|
|
191
|
-
* @satisfies REQ-300, REQ-301, REQ-302, REQ-303, REQ-357, REQ-358
|
|
207
|
+
* @return {ReqReferencesCommandExecutionResult} Per-operation outcome facts including `referencesChanged` and `commitCreated`.
|
|
208
|
+
* @throws {ReqError} Throws when reference generation, post-run content readback, staging, guarded commit creation, or cleanliness verification fails.
|
|
209
|
+
* @satisfies REQ-300, REQ-301, REQ-302, REQ-303, REQ-357, REQ-358, REQ-436
|
|
192
210
|
*/
|
|
193
211
|
export function executeReqReferencesCommandExecution(
|
|
194
212
|
plan: ReqReferencesCommandPlan,
|
|
195
213
|
config: UseReqConfig,
|
|
196
|
-
):
|
|
214
|
+
): ReqReferencesCommandExecutionResult {
|
|
215
|
+
let previousReferencesContent: string | undefined;
|
|
216
|
+
try {
|
|
217
|
+
previousReferencesContent = fs.readFileSync(plan.referencesPath, "utf8");
|
|
218
|
+
} catch {
|
|
219
|
+
previousReferencesContent = undefined;
|
|
220
|
+
}
|
|
197
221
|
runReferences(plan.basePath, config);
|
|
222
|
+
let regeneratedReferencesContent: string | undefined;
|
|
223
|
+
try {
|
|
224
|
+
regeneratedReferencesContent = fs.readFileSync(plan.referencesPath, "utf8");
|
|
225
|
+
} catch {
|
|
226
|
+
regeneratedReferencesContent = undefined;
|
|
227
|
+
}
|
|
228
|
+
if (regeneratedReferencesContent === undefined) {
|
|
229
|
+
throw new ReqError(`ERROR: Unable to read the regenerated REFERENCES.md document at ${plan.referencesPath}.`, 1);
|
|
230
|
+
}
|
|
231
|
+
const referencesChanged = regeneratedReferencesContent !== previousReferencesContent;
|
|
198
232
|
const addTargetPath = getGitAddTargetPath(plan.gitPath, plan.referencesPath);
|
|
199
233
|
const addResult = runCapture(["git", "add", "--", addTargetPath], plan.gitPath);
|
|
200
234
|
if (addResult.error || addResult.status !== 0) {
|
|
201
235
|
const diagnostic = addResult.stderr.trim() || addResult.error?.message || "unknown error";
|
|
202
236
|
throw new ReqError(`ERROR: git add failed for ${addTargetPath}: ${diagnostic}`, 1);
|
|
203
237
|
}
|
|
204
|
-
runGuardedGitCommit(plan.gitPath, [addTargetPath], plan.commitMessage);
|
|
238
|
+
const commitCreated = runGuardedGitCommit(plan.gitPath, [addTargetPath], plan.commitMessage);
|
|
205
239
|
const residualStatusLines = listResidualGitStatusLines(plan.basePath, plan.gitPath, config);
|
|
206
240
|
if (residualStatusLines.length > 0) {
|
|
207
241
|
throw new ReqError("ERROR: Git repository is not clean after req-references commit.", 1);
|
|
208
242
|
}
|
|
243
|
+
return {
|
|
244
|
+
referencesPath: plan.referencesPath,
|
|
245
|
+
referencesChanged,
|
|
246
|
+
commitMessage: plan.commitMessage,
|
|
247
|
+
commitCreated,
|
|
248
|
+
};
|
|
209
249
|
}
|
package/src/index.ts
CHANGED
|
@@ -3480,11 +3480,11 @@ function registerReqResetCommand(
|
|
|
3480
3480
|
|
|
3481
3481
|
/**
|
|
3482
3482
|
* @brief Registers the specialized `req-references` slash command.
|
|
3483
|
-
* @details Registers the non-agentic references-maintenance command that rejects non-`idle` invocations by transitioning workflow state to `error` before direct execution, otherwise reuses slash-command-owned git validation, transitions workflow state through `checking|running|idle`, regenerates `REFERENCES.md` directly from configured source directories, stages only the generated file, creates the fixed-message git commit, verifies repository cleanliness, and
|
|
3483
|
+
* @details Registers the non-agentic references-maintenance command that rejects non-`idle` invocations by transitioning workflow state to `error` before direct execution, otherwise reuses slash-command-owned git validation, transitions workflow state through `checking|running|idle`, regenerates `REFERENCES.md` directly from configured source directories, stages only the generated file, creates the fixed-message git commit, verifies repository cleanliness, and emits execution notifications that conform to the operations actually performed — one stating whether the references file was updated or already matched the generated content and one stating whether the fixed-message commit was created or skipped — regardless of the update and commit outcomes, without starting an LLM session or creating a worktree. Runtime is dominated by git subprocess execution plus source-summary generation. Side effects include command registration, status-controller mutation, filesystem writes, git index/history mutation, and user notifications.
|
|
3484
3484
|
* @param[in] pi {ExtensionAPI} Active extension API instance.
|
|
3485
3485
|
* @param[in,out] statusController {PiUsereqStatusController} Mutable status controller.
|
|
3486
3486
|
* @return {void} No return value.
|
|
3487
|
-
* @satisfies REQ-200, REQ-221, REQ-224, REQ-298, REQ-299, REQ-300, REQ-301, REQ-302, REQ-303
|
|
3487
|
+
* @satisfies REQ-200, REQ-221, REQ-224, REQ-298, REQ-299, REQ-300, REQ-301, REQ-302, REQ-303, REQ-437, REQ-438
|
|
3488
3488
|
*/
|
|
3489
3489
|
function registerReqReferencesCommand(
|
|
3490
3490
|
pi: ExtensionAPI,
|
|
@@ -3510,10 +3510,25 @@ function registerReqReferencesCommand(
|
|
|
3510
3510
|
try {
|
|
3511
3511
|
const executionPlan = prepareReqReferencesCommandExecution(projectBase, config);
|
|
3512
3512
|
setPiUsereqWorkflowState(statusController, "running", ctx);
|
|
3513
|
-
executeReqReferencesCommandExecution(executionPlan, config);
|
|
3513
|
+
const executionResult = executeReqReferencesCommandExecution(executionPlan, config);
|
|
3514
3514
|
setPiUsereqWorkflowState(statusController, "idle", ctx);
|
|
3515
|
+
const referencesDisplayPath = formatRuntimePathForDisplay(executionResult.referencesPath);
|
|
3515
3516
|
ctx.ui.notify(
|
|
3516
|
-
|
|
3517
|
+
executionResult.referencesChanged
|
|
3518
|
+
? `req-references: Updated ${referencesDisplayPath}.`
|
|
3519
|
+
: `req-references: ${referencesDisplayPath} already matches the generated references content; no file update was needed.`,
|
|
3520
|
+
"info",
|
|
3521
|
+
);
|
|
3522
|
+
ctx.ui.notify(
|
|
3523
|
+
executionResult.commitCreated
|
|
3524
|
+
? `req-references: Created commit ${executionResult.commitMessage}`
|
|
3525
|
+
: "req-references: Skipped the git commit because the references content already matched HEAD.",
|
|
3526
|
+
"info",
|
|
3527
|
+
);
|
|
3528
|
+
ctx.ui.notify(
|
|
3529
|
+
executionResult.referencesChanged && executionResult.commitCreated
|
|
3530
|
+
? `SUCCESS: Updated ${referencesDisplayPath} and committed changes.`
|
|
3531
|
+
: `SUCCESS: req-references completed with a clean repository (${executionResult.referencesChanged ? "references updated" : "references unchanged"}; ${executionResult.commitCreated ? "commit created" : "commit skipped"}).`,
|
|
3517
3532
|
"info",
|
|
3518
3533
|
);
|
|
3519
3534
|
notifyContextRetentionReminder(statusController, ctx);
|