@amistio/cli 0.1.46 → 0.1.48
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 +7 -5
- package/dist/index.js +106 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -29,7 +29,7 @@ Runner lifecycle controls in the web app, such as update, restart, and remove, a
|
|
|
29
29
|
|
|
30
30
|
Runner Update installs the official `@amistio/cli` package and then refreshes the runner runtime. Background runners attempt a replacement restart so the next heartbeat reports the new CLI version. If replacement restart metadata is missing or restart fails after a successful install, the old runner still stops and reports manual restart guidance instead of continuing to heartbeat the stale runtime. Foreground `amistio run --watch` sessions stop after a successful install with restart guidance; start the command again to load the updated package.
|
|
31
31
|
|
|
32
|
-
Current runners advertise the work kinds they can claim. Older runners that do not send this capability can continue legacy brain generation, implementation, and plan revision work, but they will skip source-aware assistant, impact-preview, semantic brain consolidation, project-context refresh, issue-diagnosis, app-evaluation, security-posture, Test-quality, implementation-Test-gate, implementation-verification, and prompt-batch work until updated. Normal runner polling also refreshes
|
|
32
|
+
Current runners advertise the work kinds they can claim. Older runners that do not send this capability can continue legacy brain generation, implementation, and plan revision work, but they will skip source-aware assistant, impact-preview, semantic brain consolidation, project-context refresh, issue-diagnosis, app-evaluation, security-posture, Test-quality, implementation-Test-gate, implementation-verification, and prompt-batch work until updated. Normal runner polling also refreshes self-maintenance health in the Evaluate panel with bounded counts, trend, and safe record IDs for operational drift; it does not upload source, full document bodies, secrets, commands, or local paths. With repository autopilot enabled and an online compatible runner, self-maintenance may requeue only backend-proven safe failed or blocked work; repeated blockers and unsafe rows stay held for review.
|
|
33
33
|
|
|
34
34
|
Prompt batches are first-class `promptBatch` work items for many approved prompts that should reach the worker together. The CLI claims one manifest, executes child prompts sequentially, reports per-child status back to Amistio, and stops according to the batch policy. This preserves auditability while avoiding repeated one-prompt handoffs; it is not shell-script batching, terminal loops, or hidden chat concatenation.
|
|
35
35
|
|
|
@@ -37,7 +37,7 @@ Tool session reuse is bounded. One-shot tool sessions close after successful com
|
|
|
37
37
|
|
|
38
38
|
Repository brain auto-sync is disabled until the repository link option is enabled in the app. After pairing, run `amistio sync watch` from the paired checkout to push recognized external brain Markdown/MDX files and explicit HTML artifacts under `docs/html/<area>/`, including local ADRs, plans, prompts, workflows, memory, context, architecture, and feature docs, to the app for review. Markdown is the default generation format; HTML appears only when a runner or user explicitly generated an HTML artifact. `amistio run --watch` also runs the same cycle between work polls when the option is enabled. The CLI skips templates, unsupported paths, oversized files, unchanged managed docs, and conflicts instead of silently overwriting web state.
|
|
39
39
|
|
|
40
|
-
Repository autopilot is disabled until the repository link option is enabled in the app. When enabled, Amistio can attach an audited low-risk autopilot authorization to eligible runner work, including generated brain approval, impact preview, issue diagnosis, security posture scan, app evaluation cleanup, low-risk implementation handoff, requeue, and implementation verification. The Runner panel shows and updates safe work scopes, allowed candidate types, max risk, optional runner binding, daily/concurrent/failure budgets, expiry/review/cooldown windows, and pause state. The CLI shows authorization id, candidate id/type, outcome, policy version, and work kind in `amistio work list`, claim logs, runner prompts, and milestone activity. Autopilot does not widen local runner permissions: pairing, supported work kinds, runner identity, Git worktree isolation, redaction, local-tool permission controls, and unsafe/review-required/blocked/paused/budget stops still apply.
|
|
40
|
+
Repository autopilot is disabled until the repository link option is enabled in the app. When enabled, Amistio can attach an audited low-risk autopilot authorization to eligible runner work, including generated brain approval, impact preview, issue diagnosis, security posture scan, app evaluation cleanup, low-risk implementation handoff, safe requeue, and implementation verification. The Runner panel shows and updates safe work scopes, allowed candidate types, max risk, optional runner binding, daily/concurrent/failure budgets, expiry/review/cooldown windows, and pause state. The CLI shows authorization id, candidate id/type, outcome, policy version, and work kind in `amistio work list`, claim logs, runner prompts, and milestone activity. Autopilot does not widen local runner permissions: pairing, supported work kinds, runner identity, Git worktree isolation, redaction, local-tool permission controls, and unsafe/review-required/blocked/repeated-blocker/paused/budget stops still apply.
|
|
41
41
|
|
|
42
42
|
After pairing, confirm that at least one local AI tool is available:
|
|
43
43
|
|
|
@@ -84,9 +84,9 @@ When `--tool codex` uses the Codex SDK, intermediate progress can be quiet until
|
|
|
84
84
|
|
|
85
85
|
The runner advertises its supported work kinds in heartbeats. Current runners can claim read-only `projectContextRefresh` jobs from the workspace Context panel and create due runner-driven refreshes when no fresh approved map exists. Context refreshes inspect the paired checkout locally without modifying files and submit only bounded summaries, slices, entities, relations, safe citations, confidence, freshness, and repo-relative paths. If a submitted context refresh contains unsafe evidence, unsafe paths, or a map too large to store safely, Amistio marks the refresh failed with a safe reason instead of storing the rejected raw result. Approved maps are reused as context packs for source-aware assistant and impact-preview work. Current runners can also claim read-only issue diagnosis jobs from the web Issues panel, generate root-cause analysis and a proposed fix, and submit that result without modifying source. They can claim manual read-only `appEvaluationScan` jobs from the workspace Evaluate panel and create at most one due hourly evaluation during normal watch/background polling when app evaluation is enabled for the repository link. Evaluation results contain bounded summaries, safe evidence, suggested actions, lifecycle proposals, and repo-relative paths only. Current runners can also claim manual read-only `securityPostureScan` jobs from the workspace Security panel and create due daily posture checks during normal watch/background polling. Security scan results contain bounded summaries, standard references, safe evidence, and repo-relative paths only. Current runners can claim manual read-only `testQualityScan` jobs from the workspace Test panel and create one due daily Test scan per repository when Test quality is enabled. Test scans run only existing lint, typecheck, test, coverage, build, or verify commands and submit bounded command summaries, coverage summaries, safe findings, blocked reasons, warnings, and repo-relative paths. Missing tests, missing coverage, low coverage, failing checks, flaky tests, and test gaps create reviewable plan-backed findings in the app. Current runners also claim `implementationTestGate` jobs before implementation completion, PR handoff, or runner-managed push; a passing gate is required unless the web Test panel records an audited override. Blocked implementation Test gates submit structured Test findings, such as `blockedEnvironment`, with safe evidence, a suggested action, and a verification plan. Current runners can claim read-only `implementationVerification` jobs from Tasks to prove whether completed implementation work actually landed; verification submits bounded acceptance-criteria evidence, checks, gaps, outcome, and recommendation without mutating source. Source, secrets, environment variables, command lines, process lists, credentials, provider sessions, and arbitrary local paths stay local. Implementation or cleanup is queued separately only after the user approves an issue analysis, app evaluation finding, security remediation plan, or Test quality plan in the app.
|
|
86
86
|
|
|
87
|
-
Approved implementation work uses Git as the handoff boundary. During worktree preflight, the runner locally copies eligible ignored root dotenv files such as `.env.local` or `.env.test.local` from the paired checkout into the implementation worktree when the target is missing and ignored, so local tests can use the same machine configuration. Dotenv values, variable names, file contents, and local paths are not uploaded to Amistio, and copied dotenv files stay ignored so PR handoff does not commit them. Before local AI/tool execution starts, implementation work checks PR handoff readiness: GitHub remote support, default-branch fetch, Git commit identity, local `gh` authentication, and repository visibility. After the local tool completes successfully, the runner materializes approved Markdown, MDX, and HTML project-brain artifacts for the same work scope into the isolated worktree before final Git status
|
|
87
|
+
Approved implementation work uses Git as the handoff boundary. During worktree preflight, the runner locally copies eligible ignored root dotenv files such as `.env.local` or `.env.test.local` from the paired checkout into the implementation worktree when the target is missing and ignored, so local tests can use the same machine configuration. Dotenv values, variable names, file contents, and local paths are not uploaded to Amistio, and copied dotenv files stay ignored so PR handoff does not commit them. Before local AI/tool execution starts, implementation work checks PR handoff readiness: GitHub remote support, default-branch fetch, Git commit identity, local `gh` authentication, and repository visibility. After the local tool completes successfully, the runner materializes approved Markdown, MDX, and HTML project-brain artifacts for the same work scope into the isolated worktree before final Git status, then classifies the final diff. Source-implementation work must include source, config, test, or other implementation-affecting changes before the runner opens or reuses a PR. If only project-brain or documentation artifacts changed, Amistio reports no implementation produced and preserves recovery choices instead of opening a misleading implementation PR. Explicit docs-only work can still create docs-only PRs. No-change completion requires no source changes and no approved artifact changes, and runner-created no-change worktrees are removed after final clean checks. Prepare the runner machine with Git commit identity, fetch/push permission to the linked remote, and `gh auth status`. If artifact materialization, commit, fetch/rebase, push, or PR creation fails, the work item is blocked with safe recovery choices; source files and patches are not uploaded to Amistio. The Work panel can queue scoped Retry handoff or Retry cleanup commands only to the runner that owns the preserved worktree for the same work item, branch, and worktree key. Retry handoff can publish a clean preserved local-only implementation commit without rerunning the implementation prompt. Rebase conflicts capture bounded repo-relative conflict files and try `git rebase --abort` so the implementation branch can be retried or manually reviewed without leaving an active rebase. Dirty, unmerged, or ambiguous worktrees are preserved rather than discarded.
|
|
88
88
|
|
|
89
|
-
Failed or stale work can be requeued from the web Tasks panel. Requeue creates a new linked work attempt and preserves the original terminal attempt for audit history; Requeue
|
|
89
|
+
Failed or stale work can be requeued from the web Tasks panel. Requeue creates a new linked work attempt and preserves the original terminal attempt for audit history; Requeue safe sends one backend batch that recomputes safe candidates, reports already-active and skipped rows, and still uses linked attempts. Requeue is blocked while equivalent work is already active, when the paired runner does not advertise the needed work kind, or when the latest linked attempt repeats the same sanitized blocker fingerprint. Repeated runner setup, handoff, policy, verification, and worktree blockers require root-cause repair before another linked attempt. Completed implementation status is separate from proof: queue `implementationVerification` from Tasks when a plan needs source-aware evidence before cleanup or implementation status decisions.
|
|
90
90
|
|
|
91
91
|
Runner setup and local-tool execution use bounded failure controls. During Git worktree preflight, `amistio run --watch` repairs safe stale Git registrations when the target worktree directory is missing and Git marks the registration prunable; dirty, present, or ambiguous worktrees are preserved. Other Git worktree preflight failures are retried by releasing the claim for another attempt, then fail the work item after `--max-preflight-attempts` attempts, defaulting to 3. Active local-tool runs renew the work lease, and `--tool-timeout-seconds` caps tool execution, defaulting to 1800 seconds.
|
|
92
92
|
|
|
@@ -100,7 +100,9 @@ Known validation failures such as `unsafe_context_path` are printed with attenti
|
|
|
100
100
|
|
|
101
101
|
If watch mode reports that the runner was forgotten by the server, run `amistio runner repair` from the paired checkout, then start `amistio run --watch` again. The repair command stores a fresh local runner ID because the default ID for a machine/project/repository is stable and can remain tombstoned. Use `--clear-credential` only when the Runner panel tells you to create a fresh pairing code.
|
|
102
102
|
|
|
103
|
-
App-evaluation result finalization rejections print safe validation paths and preserve the local finalization evidence without exposing raw source or secrets. If a structured app-evaluation result is rejected, update and restart the runner, confirm the web/API deployment is current, and retry
|
|
103
|
+
App-evaluation and impact-preview result finalization rejections print safe validation paths and preserve the local finalization evidence without exposing raw source or secrets. If a structured app-evaluation or impact-preview result is rejected, update and restart the runner, confirm the web/API deployment is current, and retry before acting on cleanup, implementation, or risk recommendations. Accepted impact-preview results that cannot be stored safely are marked failed with a bounded reason instead of uploading raw source or secrets.
|
|
104
|
+
|
|
105
|
+
When a newer manual or hourly app evaluation is queued for the same repository, Amistio marks older queued or running app-evaluation scans stale and closes their scan work without deleting historical scans, findings, or generated plans.
|
|
104
106
|
|
|
105
107
|
When brain generation or plan revision output is parsed but the Amistio API is temporarily unavailable during finalization, the runner keeps a safe pending result envelope in user-level Amistio config and replays it before claiming more work. The envelope uses a stable idempotency key and does not store raw tool stdout, provider sessions, credentials, or arbitrary local paths.
|
|
106
108
|
|
package/dist/index.js
CHANGED
|
@@ -154,7 +154,8 @@ var autopilotGuardCheckSchema = z.object({
|
|
|
154
154
|
status: z.enum(["passed", "failed", "warning"]),
|
|
155
155
|
summary: z.string().trim().min(1).max(600)
|
|
156
156
|
});
|
|
157
|
-
var
|
|
157
|
+
var implementationExpectedOutcomeSchema = z.enum(["sourceImplementation", "docsOnly", "analysisOnly", "verificationOnly"]);
|
|
158
|
+
var implementationHandoffStatusSchema = z.enum(["notStarted", "noChanges", "prReady", "blocked", "failed", "noImplementationProduced"]);
|
|
158
159
|
var implementationHandoffCleanupStatusSchema = z.enum(["notApplicable", "pending", "completed", "failed"]);
|
|
159
160
|
var implementationHandoffRecoveryCategorySchema = z.enum(["noChangeCleaned", "rebaseConflict", "dirtyWorktree", "unresolvedConflicts", "providerBlocked", "cleanupRetryAvailable", "manualReview", "artifactBlocked", "nonGithubProvider"]);
|
|
160
161
|
var implementationHandoffRecoveryActionSchema = z.enum(["retryHandoff", "requeueFreshAttempt", "keepForManualRepair", "cleanNoChangeWorktree", "markSupersededNoChanges", "exportHandoffDetails", "retryCleanup"]);
|
|
@@ -223,9 +224,19 @@ var implementationHandoffRecoverySchema = z.object({
|
|
|
223
224
|
var implementationVerificationOutcomeSchema = z.enum(["verifiedImplemented", "partiallyImplemented", "notImplemented", "inconclusive", "verificationBlocked"]);
|
|
224
225
|
var implementationVerificationStatusSchema = z.enum(["queued", "running", "completed", "failed", "blocked", "stale"]);
|
|
225
226
|
var implementationProofStatusSchema = z.enum(["unverified", "verificationQueued", "verificationRunning", "verifiedImplemented", "partiallyImplemented", "notImplemented", "inconclusive", "verificationBlocked", "humanOverride"]);
|
|
227
|
+
var implementationHandoffDiffClassificationSchema = z.enum(["noChanges", "sourceImplementation", "sourceAndArtifacts", "projectBrainOnly", "docsOnly"]);
|
|
228
|
+
var implementationHandoffDiffSummarySchema = z.object({
|
|
229
|
+
classification: implementationHandoffDiffClassificationSchema,
|
|
230
|
+
sourceImplementationFileCount: z.number().int().nonnegative().default(0),
|
|
231
|
+
projectBrainArtifactFileCount: z.number().int().nonnegative().default(0),
|
|
232
|
+
documentationFileCount: z.number().int().nonnegative().default(0),
|
|
233
|
+
fileSamples: z.array(safeRepoPathSchema).max(25).default([])
|
|
234
|
+
}).strict();
|
|
226
235
|
var implementationHandoffSchema = z.object({
|
|
227
236
|
provider: z.string().trim().min(1).max(80).optional(),
|
|
228
237
|
status: implementationHandoffStatusSchema,
|
|
238
|
+
expectedOutcome: implementationExpectedOutcomeSchema.optional(),
|
|
239
|
+
diffSummary: implementationHandoffDiffSummarySchema.optional(),
|
|
229
240
|
baseBranch: z.string().trim().min(1).max(200).optional(),
|
|
230
241
|
headBranch: z.string().trim().min(1).max(200).optional(),
|
|
231
242
|
remoteName: z.string().trim().min(1).max(80).optional(),
|
|
@@ -858,6 +869,7 @@ var workItemSchema = baseItemSchema.extend({
|
|
|
858
869
|
implementationTestGateId: z.string().min(1).optional(),
|
|
859
870
|
projectContextRefreshId: z.string().min(1).optional(),
|
|
860
871
|
contextMissId: z.string().min(1).optional(),
|
|
872
|
+
changeId: z.string().min(1).optional(),
|
|
861
873
|
sourceWorkItemId: z.string().min(1).optional(),
|
|
862
874
|
requeueReason: z.string().trim().min(1).max(600).optional(),
|
|
863
875
|
requeuedByUserId: z.string().min(1).optional(),
|
|
@@ -868,6 +880,7 @@ var workItemSchema = baseItemSchema.extend({
|
|
|
868
880
|
implementationProofStatus: implementationProofStatusSchema.optional(),
|
|
869
881
|
implementationVerificationOutcome: implementationVerificationOutcomeSchema.optional(),
|
|
870
882
|
implementationVerificationResultId: z.string().min(1).optional(),
|
|
883
|
+
implementationExpectedOutcome: implementationExpectedOutcomeSchema.optional(),
|
|
871
884
|
humanImplementationOverrideReason: z.string().trim().min(1).max(1e3).optional(),
|
|
872
885
|
repositoryLinkId: z.string().min(1).optional(),
|
|
873
886
|
reviewThreadId: z.string().min(1).optional(),
|
|
@@ -7891,6 +7904,7 @@ function createAppEvaluationScanPrompt(workItem, context) {
|
|
|
7891
7904
|
"- When lifecycle metadata disagrees across indexes, frontmatter, feature specs, ADRs, and implementation evidence, cite the conflict and propose a metadata correction or verification step instead of archival/removal.",
|
|
7892
7905
|
"- Check missing memory or workflow updates when repeated lessons or operational rules are visible.",
|
|
7893
7906
|
"- Check release readiness, UX, accessibility, performance, reliability, and security-posture follow-through at a summary level.",
|
|
7907
|
+
"- For release, deploy, or publish readiness, do not treat the mere presence of a workflow, script, or documented verification gate as proof. If fresh successful root verification evidence is missing for the current release candidate, report releaseReadiness and recommend refreshing the gate before release actions.",
|
|
7894
7908
|
"- Prefer repository-documented verification commands over ad hoc package-script inference.",
|
|
7895
7909
|
"- For this Amistio monorepo, if plain Corepack pnpm fails before scripts with spawnSync pnpm ENOENT, retry the documented command corepack pnpm --config.verify-deps-before-run=false verify before declaring whole-app verification blocked.",
|
|
7896
7910
|
"",
|
|
@@ -9696,6 +9710,7 @@ import { execFile as execFile6 } from "node:child_process";
|
|
|
9696
9710
|
import path18 from "node:path";
|
|
9697
9711
|
import { promisify as promisify6 } from "node:util";
|
|
9698
9712
|
var execFileAsync6 = promisify6(execFile6);
|
|
9713
|
+
var DEFAULT_IMPLEMENTATION_EXPECTED_OUTCOME = "sourceImplementation";
|
|
9699
9714
|
async function checkImplementationHandoffReadiness(input) {
|
|
9700
9715
|
const run = input.commandRunner ?? defaultCommandRunner;
|
|
9701
9716
|
const headBranch = input.worktreeIsolation?.branch ?? input.workItem.executionBranch;
|
|
@@ -9776,6 +9791,7 @@ async function completeImplementationHandoff(input) {
|
|
|
9776
9791
|
const run = input.commandRunner ?? defaultCommandRunner;
|
|
9777
9792
|
const headBranch = input.worktreeIsolation?.branch ?? input.workItem.executionBranch;
|
|
9778
9793
|
const baseBranch = input.baseBranch ?? input.repositoryLink?.defaultBranch ?? "main";
|
|
9794
|
+
const expectedOutcome = input.workItem.implementationExpectedOutcome ?? DEFAULT_IMPLEMENTATION_EXPECTED_OUTCOME;
|
|
9779
9795
|
if (!headBranch) {
|
|
9780
9796
|
return blockedHandoff({ baseBranch, message: "Implementation handoff needs an execution branch before it can create a pull request." });
|
|
9781
9797
|
}
|
|
@@ -9810,6 +9826,7 @@ async function completeImplementationHandoff(input) {
|
|
|
9810
9826
|
});
|
|
9811
9827
|
}
|
|
9812
9828
|
const status = await gitOutput2(run, input.worktreePath, ["status", "--porcelain=v1", "-z", "--untracked-files=all"]);
|
|
9829
|
+
const diffSummary = classifyHandoffDiff(status);
|
|
9813
9830
|
if (!status) {
|
|
9814
9831
|
if (input.includeCleanLocalCommits) {
|
|
9815
9832
|
const cleanBranchHandoff = await completeCleanLocalCommitHandoff({ artifactResult, baseBranch, common, headBranch, input, run });
|
|
@@ -9821,6 +9838,8 @@ async function completeImplementationHandoff(input) {
|
|
|
9821
9838
|
return {
|
|
9822
9839
|
...common,
|
|
9823
9840
|
status: "noChanges",
|
|
9841
|
+
expectedOutcome,
|
|
9842
|
+
diffSummary,
|
|
9824
9843
|
cleanupStatus: cleanup2.status,
|
|
9825
9844
|
...cleanup2.message ? { cleanupMessage: cleanup2.message } : {},
|
|
9826
9845
|
artifacts: artifactResult,
|
|
@@ -9834,6 +9853,24 @@ async function completeImplementationHandoff(input) {
|
|
|
9834
9853
|
message: cleanup2.status === "completed" ? "Local execution completed with no repository changes to hand off and the local worktree was cleaned up." : "Local execution completed with no repository changes to hand off."
|
|
9835
9854
|
};
|
|
9836
9855
|
}
|
|
9856
|
+
if (expectedOutcome === "sourceImplementation" && diffSummary.sourceImplementationFileCount === 0) {
|
|
9857
|
+
return {
|
|
9858
|
+
...common,
|
|
9859
|
+
status: "noImplementationProduced",
|
|
9860
|
+
expectedOutcome,
|
|
9861
|
+
diffSummary,
|
|
9862
|
+
cleanupStatus: "pending",
|
|
9863
|
+
artifacts: artifactResult,
|
|
9864
|
+
recovery: handoffRecovery(input, {
|
|
9865
|
+
category: "manualReview",
|
|
9866
|
+
availableActions: ["requeueFreshAttempt", "keepForManualRepair", "exportHandoffDetails"],
|
|
9867
|
+
cleanupEligible: false,
|
|
9868
|
+
rebaseAbortStatus: "notApplicable",
|
|
9869
|
+
summary: "The runner finished, but the final diff contains only project-brain or documentation artifacts. Requeue implementation or explicitly approve this as docs-only before opening a PR."
|
|
9870
|
+
}),
|
|
9871
|
+
message: "Implementation expected source, config, or test changes, but only documentation or project-brain artifacts changed. No implementation PR was opened."
|
|
9872
|
+
};
|
|
9873
|
+
}
|
|
9837
9874
|
const remoteName = await resolveRemoteName(run, input.worktreePath);
|
|
9838
9875
|
const remoteUrl = await gitOutput2(run, input.worktreePath, ["remote", "get-url", remoteName]);
|
|
9839
9876
|
const provider = input.repositoryLink?.provider ?? inferProvider(remoteUrl);
|
|
@@ -9842,6 +9879,8 @@ async function completeImplementationHandoff(input) {
|
|
|
9842
9879
|
...common,
|
|
9843
9880
|
provider,
|
|
9844
9881
|
remoteName,
|
|
9882
|
+
expectedOutcome,
|
|
9883
|
+
diffSummary,
|
|
9845
9884
|
artifacts: artifactResult,
|
|
9846
9885
|
message: "Automated pull request handoff currently requires a GitHub remote. Commit and push manually, or link a GitHub repository.",
|
|
9847
9886
|
recovery: handoffRecovery(input, {
|
|
@@ -9869,6 +9908,8 @@ async function completeImplementationHandoff(input) {
|
|
|
9869
9908
|
return {
|
|
9870
9909
|
provider: "github",
|
|
9871
9910
|
status: "prReady",
|
|
9911
|
+
expectedOutcome,
|
|
9912
|
+
diffSummary,
|
|
9872
9913
|
baseBranch,
|
|
9873
9914
|
headBranch,
|
|
9874
9915
|
remoteName,
|
|
@@ -10174,6 +10215,70 @@ function blockedHandoff(input) {
|
|
|
10174
10215
|
...input
|
|
10175
10216
|
};
|
|
10176
10217
|
}
|
|
10218
|
+
function classifyHandoffDiff(status) {
|
|
10219
|
+
const files = status ? safeRepoPathsFromStatus(status) : [];
|
|
10220
|
+
let sourceImplementationFileCount = 0;
|
|
10221
|
+
let projectBrainArtifactFileCount = 0;
|
|
10222
|
+
let documentationFileCount = 0;
|
|
10223
|
+
for (const file of files) {
|
|
10224
|
+
if (isProjectBrainArtifactPath(file)) {
|
|
10225
|
+
projectBrainArtifactFileCount += 1;
|
|
10226
|
+
} else if (isDocumentationPath(file)) {
|
|
10227
|
+
documentationFileCount += 1;
|
|
10228
|
+
} else {
|
|
10229
|
+
sourceImplementationFileCount += 1;
|
|
10230
|
+
}
|
|
10231
|
+
}
|
|
10232
|
+
const classification = sourceImplementationFileCount > 0 ? projectBrainArtifactFileCount > 0 || documentationFileCount > 0 ? "sourceAndArtifacts" : "sourceImplementation" : projectBrainArtifactFileCount > 0 ? documentationFileCount > 0 ? "docsOnly" : "projectBrainOnly" : documentationFileCount > 0 ? "docsOnly" : "noChanges";
|
|
10233
|
+
return {
|
|
10234
|
+
classification,
|
|
10235
|
+
sourceImplementationFileCount,
|
|
10236
|
+
projectBrainArtifactFileCount,
|
|
10237
|
+
documentationFileCount,
|
|
10238
|
+
fileSamples: files.slice(0, 25)
|
|
10239
|
+
};
|
|
10240
|
+
}
|
|
10241
|
+
function safeRepoPathsFromStatus(status) {
|
|
10242
|
+
const paths = [];
|
|
10243
|
+
const parts = status.split("\0").filter(Boolean);
|
|
10244
|
+
for (let index = 0; index < parts.length; index += 1) {
|
|
10245
|
+
const entry = parts[index];
|
|
10246
|
+
const statusCode = entry.slice(0, 2);
|
|
10247
|
+
const parsedPath = entry.slice(entry[2] === " " ? 3 : 0).trim();
|
|
10248
|
+
if (statusCode.includes("R") || statusCode.includes("C")) {
|
|
10249
|
+
const nextPath = parts[index + 1];
|
|
10250
|
+
if (nextPath && !/^[ MADRCU?!]{2} /.test(nextPath)) {
|
|
10251
|
+
index += 1;
|
|
10252
|
+
addSafePath(paths, nextPath.trim());
|
|
10253
|
+
continue;
|
|
10254
|
+
}
|
|
10255
|
+
}
|
|
10256
|
+
addSafePath(paths, parsedPath);
|
|
10257
|
+
}
|
|
10258
|
+
return [...new Set(paths)].slice(0, 100);
|
|
10259
|
+
}
|
|
10260
|
+
function addSafePath(paths, repoPath) {
|
|
10261
|
+
if (!repoPath || repoPath.startsWith("/") || /^[A-Za-z]:[\\/]/.test(repoPath) || repoPath.split(/[\\/]+/).includes("..")) {
|
|
10262
|
+
return;
|
|
10263
|
+
}
|
|
10264
|
+
paths.push(repoPath);
|
|
10265
|
+
}
|
|
10266
|
+
function isProjectBrainArtifactPath(repoPath) {
|
|
10267
|
+
return [
|
|
10268
|
+
"docs/architecture/",
|
|
10269
|
+
"docs/context/",
|
|
10270
|
+
"docs/decisions/",
|
|
10271
|
+
"docs/features/",
|
|
10272
|
+
"docs/memory/",
|
|
10273
|
+
"docs/plans/",
|
|
10274
|
+
"docs/prompts/",
|
|
10275
|
+
"docs/workflows/"
|
|
10276
|
+
].some((prefix) => repoPath.startsWith(prefix));
|
|
10277
|
+
}
|
|
10278
|
+
function isDocumentationPath(repoPath) {
|
|
10279
|
+
const basename = repoPath.split("/").pop() ?? repoPath;
|
|
10280
|
+
return /\.(?:md|mdx|html)$/i.test(basename);
|
|
10281
|
+
}
|
|
10177
10282
|
function inferProvider(remoteUrl) {
|
|
10178
10283
|
try {
|
|
10179
10284
|
return parseRepositoryCloneUrl(remoteUrl).provider ?? "other";
|