agentplane 0.3.5 → 0.3.7
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 +103 -75
- package/assets/AGENTS.md +4 -2
- package/bin/dist-guard.js +13 -3
- package/bin/runtime-watch.d.ts +1 -0
- package/bin/runtime-watch.js +22 -5
- package/bin/stale-dist-policy.js +9 -2
- package/dist/.build-manifest.json +251 -821
- package/dist/adapters/task-backend/task-backend-adapter.d.ts +2 -2
- package/dist/adapters/task-backend/task-backend-adapter.d.ts.map +1 -1
- package/dist/adapters/task-backend/task-backend-adapter.js +2 -2
- package/dist/backends/task-backend/local-backend.d.ts +7 -5
- package/dist/backends/task-backend/local-backend.d.ts.map +1 -1
- package/dist/backends/task-backend/local-backend.js +79 -7
- package/dist/backends/task-backend/redmine/env.d.ts +1 -1
- package/dist/backends/task-backend/redmine/env.d.ts.map +1 -1
- package/dist/backends/task-backend/redmine/env.js +3 -0
- package/dist/backends/task-backend/redmine/inspect.d.ts +11 -0
- package/dist/backends/task-backend/redmine/inspect.d.ts.map +1 -0
- package/dist/backends/task-backend/redmine/inspect.js +75 -0
- package/dist/backends/task-backend/redmine/mapping.d.ts.map +1 -1
- package/dist/backends/task-backend/redmine/mapping.js +21 -2
- package/dist/backends/task-backend/redmine/state.d.ts +17 -0
- package/dist/backends/task-backend/redmine/state.d.ts.map +1 -0
- package/dist/backends/task-backend/redmine/state.js +95 -0
- package/dist/backends/task-backend/redmine-backend.d.ts +10 -16
- package/dist/backends/task-backend/redmine-backend.d.ts.map +1 -1
- package/dist/backends/task-backend/redmine-backend.js +205 -15
- package/dist/backends/task-backend/shared/constants.d.ts +1 -1
- package/dist/backends/task-backend/shared/constants.js +1 -1
- package/dist/backends/task-backend/shared/record.d.ts.map +1 -1
- package/dist/backends/task-backend/shared/record.js +20 -1
- package/dist/backends/task-backend/shared/types.d.ts +42 -4
- package/dist/backends/task-backend/shared/types.d.ts.map +1 -1
- package/dist/backends/task-backend/shared.d.ts +1 -1
- package/dist/backends/task-backend/shared.d.ts.map +1 -1
- package/dist/backends/task-backend.d.ts +1 -1
- package/dist/backends/task-backend.d.ts.map +1 -1
- package/dist/backends/task-backend.test-helpers.d.ts +4 -0
- package/dist/backends/task-backend.test-helpers.d.ts.map +1 -0
- package/dist/backends/task-backend.test-helpers.js +33 -0
- package/dist/backends/task-index.d.ts.map +1 -1
- package/dist/backends/task-index.js +1 -0
- package/dist/cli/bootstrap-guide.d.ts.map +1 -1
- package/dist/cli/bootstrap-guide.js +1 -0
- package/dist/cli/command-guide.d.ts.map +1 -1
- package/dist/cli/command-guide.js +3 -2
- package/dist/cli/reason-codes.d.ts.map +1 -1
- package/dist/cli/reason-codes.js +30 -0
- package/dist/cli/run-cli/command-catalog/core.d.ts +3 -0
- package/dist/cli/run-cli/command-catalog/core.d.ts.map +1 -0
- package/dist/cli/run-cli/command-catalog/core.js +137 -0
- package/dist/cli/run-cli/command-catalog/lifecycle.d.ts +3 -0
- package/dist/cli/run-cli/command-catalog/lifecycle.d.ts.map +1 -0
- package/dist/cli/run-cli/command-catalog/lifecycle.js +52 -0
- package/dist/cli/run-cli/command-catalog/project.d.ts +3 -0
- package/dist/cli/run-cli/command-catalog/project.d.ts.map +1 -0
- package/dist/cli/run-cli/command-catalog/project.js +80 -0
- package/dist/cli/run-cli/command-catalog/shared.d.ts +19 -0
- package/dist/cli/run-cli/command-catalog/shared.d.ts.map +1 -0
- package/dist/cli/run-cli/command-catalog/shared.js +9 -0
- package/dist/cli/run-cli/command-catalog/task.d.ts +3 -0
- package/dist/cli/run-cli/command-catalog/task.d.ts.map +1 -0
- package/dist/cli/run-cli/command-catalog/task.js +85 -0
- package/dist/cli/run-cli/command-catalog.d.ts +3 -18
- package/dist/cli/run-cli/command-catalog.d.ts.map +1 -1
- package/dist/cli/run-cli/command-catalog.js +8 -337
- package/dist/cli/run-cli/commands/ide.d.ts.map +1 -1
- package/dist/cli/run-cli/commands/ide.js +64 -2
- package/dist/cli/run-cli/commands/init/ui.d.ts.map +1 -1
- package/dist/cli/run-cli/commands/init/ui.js +33 -13
- package/dist/cli/run-cli/commands/init/write-env.d.ts.map +1 -1
- package/dist/cli/run-cli/commands/init/write-env.js +12 -0
- package/dist/cli/run-cli.core.pr-flow.test-helpers.d.ts +3 -0
- package/dist/cli/run-cli.core.pr-flow.test-helpers.d.ts.map +1 -0
- package/dist/cli/run-cli.core.pr-flow.test-helpers.js +41 -0
- package/dist/cli/run-cli.core.tasks.test-helpers.d.ts +2 -0
- package/dist/cli/run-cli.core.tasks.test-helpers.d.ts.map +1 -0
- package/dist/cli/run-cli.core.tasks.test-helpers.js +6 -0
- package/dist/cli/run-cli.test-helpers.d.ts +3 -0
- package/dist/cli/run-cli.test-helpers.d.ts.map +1 -1
- package/dist/cli/run-cli.test-helpers.js +140 -6
- package/dist/commands/backend/sync.command.d.ts +5 -1
- package/dist/commands/backend/sync.command.d.ts.map +1 -1
- package/dist/commands/backend/sync.command.js +67 -3
- package/dist/commands/backend.d.ts +22 -0
- package/dist/commands/backend.d.ts.map +1 -1
- package/dist/commands/backend.js +110 -1
- package/dist/commands/commit.spec.d.ts.map +1 -1
- package/dist/commands/commit.spec.js +31 -7
- package/dist/commands/doctor/runtime.d.ts.map +1 -1
- package/dist/commands/doctor/runtime.js +3 -6
- package/dist/commands/doctor/workspace.d.ts +8 -0
- package/dist/commands/doctor/workspace.d.ts.map +1 -1
- package/dist/commands/doctor/workspace.js +127 -3
- package/dist/commands/guard/commit.command.d.ts.map +1 -1
- package/dist/commands/guard/commit.command.js +30 -6
- package/dist/commands/guard/impl/allow.d.ts +9 -0
- package/dist/commands/guard/impl/allow.d.ts.map +1 -1
- package/dist/commands/guard/impl/allow.js +26 -10
- package/dist/commands/guard/impl/commands.d.ts.map +1 -1
- package/dist/commands/guard/impl/commands.js +146 -18
- package/dist/commands/guard/impl/comment-commit.d.ts.map +1 -1
- package/dist/commands/guard/impl/comment-commit.js +2 -0
- package/dist/commands/hooks/index.d.ts.map +1 -1
- package/dist/commands/hooks/index.js +8 -35
- package/dist/commands/recipes/impl/apply.d.ts +4 -0
- package/dist/commands/recipes/impl/apply.d.ts.map +1 -1
- package/dist/commands/recipes/impl/apply.js +34 -0
- package/dist/commands/recipes/impl/commands/explain.d.ts.map +1 -1
- package/dist/commands/recipes/impl/commands/explain.js +70 -11
- package/dist/commands/recipes/impl/commands/info.d.ts.map +1 -1
- package/dist/commands/recipes/impl/commands/info.js +24 -12
- package/dist/commands/recipes/impl/commands/install.d.ts.map +1 -1
- package/dist/commands/recipes/impl/commands/install.js +32 -36
- package/dist/commands/recipes/impl/commands/list.d.ts.map +1 -1
- package/dist/commands/recipes/impl/commands/list.js +7 -4
- package/dist/commands/recipes/impl/commands/remove.d.ts.map +1 -1
- package/dist/commands/recipes/impl/commands/remove.js +9 -11
- package/dist/commands/recipes/impl/constants.d.ts +2 -0
- package/dist/commands/recipes/impl/constants.d.ts.map +1 -1
- package/dist/commands/recipes/impl/constants.js +2 -0
- package/dist/commands/recipes/impl/manifest.d.ts.map +1 -1
- package/dist/commands/recipes/impl/manifest.js +219 -23
- package/dist/commands/recipes/impl/normalize.d.ts +3 -0
- package/dist/commands/recipes/impl/normalize.d.ts.map +1 -1
- package/dist/commands/recipes/impl/normalize.js +28 -24
- package/dist/commands/recipes/impl/paths.d.ts +9 -0
- package/dist/commands/recipes/impl/paths.d.ts.map +1 -1
- package/dist/commands/recipes/impl/paths.js +10 -1
- package/dist/commands/recipes/impl/project-installed-recipes.d.ts +7 -0
- package/dist/commands/recipes/impl/project-installed-recipes.d.ts.map +1 -0
- package/dist/commands/recipes/impl/project-installed-recipes.js +102 -0
- package/dist/commands/recipes/impl/resolver.d.ts +20 -0
- package/dist/commands/recipes/impl/resolver.d.ts.map +1 -0
- package/dist/commands/recipes/impl/resolver.js +220 -0
- package/dist/commands/recipes/impl/scenario.d.ts.map +1 -1
- package/dist/commands/recipes/impl/scenario.js +40 -11
- package/dist/commands/recipes/impl/types.d.ts +145 -16
- package/dist/commands/recipes/impl/types.d.ts.map +1 -1
- package/dist/commands/recipes/install.spec.d.ts.map +1 -1
- package/dist/commands/recipes/install.spec.js +3 -2
- package/dist/commands/recipes.d.ts +6 -4
- package/dist/commands/recipes.d.ts.map +1 -1
- package/dist/commands/recipes.js +5 -3
- package/dist/commands/recipes.test-helpers.d.ts +185 -0
- package/dist/commands/recipes.test-helpers.d.ts.map +1 -0
- package/dist/commands/recipes.test-helpers.js +339 -0
- package/dist/commands/scenario/impl/commands.d.ts.map +1 -1
- package/dist/commands/scenario/impl/commands.js +192 -336
- package/dist/commands/scenario/info.command.d.ts.map +1 -1
- package/dist/commands/scenario/info.command.js +7 -2
- package/dist/commands/scenario/list.command.js +2 -2
- package/dist/commands/scenario/run.command.d.ts.map +1 -1
- package/dist/commands/scenario/run.command.js +7 -2
- package/dist/commands/shared/reconcile-check.d.ts.map +1 -1
- package/dist/commands/shared/reconcile-check.js +77 -2
- package/dist/commands/shared/task-backend.d.ts +1 -1
- package/dist/commands/shared/task-backend.d.ts.map +1 -1
- package/dist/commands/shared/task-backend.js +9 -0
- package/dist/commands/shared/task-store.d.ts +92 -2
- package/dist/commands/shared/task-store.d.ts.map +1 -1
- package/dist/commands/shared/task-store.js +405 -43
- package/dist/commands/task/block.d.ts.map +1 -1
- package/dist/commands/task/block.js +84 -46
- package/dist/commands/task/close-duplicate.d.ts.map +1 -1
- package/dist/commands/task/close-duplicate.js +12 -37
- package/dist/commands/task/close-noop.d.ts.map +1 -1
- package/dist/commands/task/close-noop.js +12 -30
- package/dist/commands/task/close-shared.d.ts +14 -0
- package/dist/commands/task/close-shared.d.ts.map +1 -0
- package/dist/commands/task/close-shared.js +73 -0
- package/dist/commands/task/comment.d.ts.map +1 -1
- package/dist/commands/task/comment.js +34 -21
- package/dist/commands/task/derive.command.d.ts +1 -0
- package/dist/commands/task/derive.command.d.ts.map +1 -1
- package/dist/commands/task/derive.command.js +15 -2
- package/dist/commands/task/derive.d.ts +1 -0
- package/dist/commands/task/derive.d.ts.map +1 -1
- package/dist/commands/task/derive.js +27 -4
- package/dist/commands/task/doc-set.command.d.ts +2 -1
- package/dist/commands/task/doc-set.command.d.ts.map +1 -1
- package/dist/commands/task/doc-set.command.js +36 -4
- package/dist/commands/task/doc-template.d.ts.map +1 -1
- package/dist/commands/task/doc-template.js +2 -7
- package/dist/commands/task/doc.command.js +1 -1
- package/dist/commands/task/doc.d.ts +2 -1
- package/dist/commands/task/doc.d.ts.map +1 -1
- package/dist/commands/task/doc.js +139 -76
- package/dist/commands/task/finish.d.ts.map +1 -1
- package/dist/commands/task/finish.js +142 -80
- package/dist/commands/task/migrate-doc.d.ts +15 -0
- package/dist/commands/task/migrate-doc.d.ts.map +1 -1
- package/dist/commands/task/migrate-doc.js +128 -43
- package/dist/commands/task/new.d.ts.map +1 -1
- package/dist/commands/task/new.js +3 -1
- package/dist/commands/task/plan-set.command.js +1 -1
- package/dist/commands/task/plan.command.d.ts +8 -0
- package/dist/commands/task/plan.command.d.ts.map +1 -0
- package/dist/commands/task/plan.command.js +37 -0
- package/dist/commands/task/plan.d.ts.map +1 -1
- package/dist/commands/task/plan.js +198 -101
- package/dist/commands/task/set-status.command.d.ts.map +1 -1
- package/dist/commands/task/set-status.command.js +1 -1
- package/dist/commands/task/set-status.d.ts.map +1 -1
- package/dist/commands/task/set-status.js +115 -35
- package/dist/commands/task/shared/dependencies.d.ts +1 -0
- package/dist/commands/task/shared/dependencies.d.ts.map +1 -1
- package/dist/commands/task/shared/dependencies.js +10 -0
- package/dist/commands/task/shared/docs.d.ts +1 -0
- package/dist/commands/task/shared/docs.d.ts.map +1 -1
- package/dist/commands/task/shared/docs.js +8 -1
- package/dist/commands/task/shared/transitions.d.ts +17 -2
- package/dist/commands/task/shared/transitions.d.ts.map +1 -1
- package/dist/commands/task/shared/transitions.js +20 -13
- package/dist/commands/task/shared.d.ts +3 -3
- package/dist/commands/task/shared.d.ts.map +1 -1
- package/dist/commands/task/shared.js +3 -3
- package/dist/commands/task/start.d.ts.map +1 -1
- package/dist/commands/task/start.js +101 -71
- package/dist/commands/task/task.command.d.ts +8 -0
- package/dist/commands/task/task.command.d.ts.map +1 -0
- package/dist/commands/task/task.command.js +71 -0
- package/dist/commands/task/verify-command-shared.d.ts +16 -0
- package/dist/commands/task/verify-command-shared.d.ts.map +1 -0
- package/dist/commands/task/verify-command-shared.js +53 -0
- package/dist/commands/task/verify-ok.command.d.ts +2 -6
- package/dist/commands/task/verify-ok.command.d.ts.map +1 -1
- package/dist/commands/task/verify-ok.command.js +8 -50
- package/dist/commands/task/verify-record.d.ts.map +1 -1
- package/dist/commands/task/verify-record.js +124 -145
- package/dist/commands/task/verify-rework.command.d.ts +2 -6
- package/dist/commands/task/verify-rework.command.d.ts.map +1 -1
- package/dist/commands/task/verify-rework.command.js +8 -50
- package/dist/commands/upgrade/apply.d.ts +2 -0
- package/dist/commands/upgrade/apply.d.ts.map +1 -1
- package/dist/commands/upgrade/apply.js +33 -1
- package/dist/commands/upgrade.command.d.ts.map +1 -1
- package/dist/commands/upgrade.command.js +25 -0
- package/dist/commands/upgrade.d.ts +1 -0
- package/dist/commands/upgrade.d.ts.map +1 -1
- package/dist/commands/upgrade.js +34 -0
- package/dist/commands/verify.spec.d.ts.map +1 -1
- package/dist/commands/verify.spec.js +3 -12
- package/dist/policy/rules/allowlist.d.ts.map +1 -1
- package/dist/policy/rules/allowlist.js +16 -4
- package/dist/policy/rules/protected-paths.d.ts.map +1 -1
- package/dist/policy/rules/protected-paths.js +6 -1
- package/dist/ports/task-backend-port.d.ts +2 -2
- package/dist/ports/task-backend-port.d.ts.map +1 -1
- package/dist/shared/agent-emoji.d.ts.map +1 -1
- package/dist/shared/protected-paths.d.ts +17 -0
- package/dist/shared/protected-paths.d.ts.map +1 -1
- package/dist/shared/protected-paths.js +59 -10
- package/dist/shared/repo-cli-version.d.ts.map +1 -1
- package/dist/shared/repo-cli-version.js +9 -3
- package/package.json +2 -2
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { parseTaskReadme, renderTaskDocFromSections } from "@agentplaneorg/core";
|
|
4
5
|
import { renderDiagnosticFinding } from "../../shared/diagnostics.js";
|
|
5
6
|
import { resolvePolicyGatewayForRepo } from "../../shared/policy-gateway.js";
|
|
6
7
|
import { GitContext } from "../shared/git-context.js";
|
|
@@ -84,7 +85,7 @@ async function readTaskDocSnapshotsFromProjection(ctx) {
|
|
|
84
85
|
return null;
|
|
85
86
|
}
|
|
86
87
|
}
|
|
87
|
-
function buildTaskReadmeMigrationFindings(tasks) {
|
|
88
|
+
export function buildTaskReadmeMigrationFindings(tasks) {
|
|
88
89
|
if (tasks.length === 0)
|
|
89
90
|
return [];
|
|
90
91
|
const legacy = tasks.filter((task) => task.doc_version !== 3);
|
|
@@ -138,7 +139,7 @@ function buildTaskReadmeMigrationFindings(tasks) {
|
|
|
138
139
|
}),
|
|
139
140
|
];
|
|
140
141
|
}
|
|
141
|
-
async function checkTaskReadmeMigrationState(repoRoot, ctx) {
|
|
142
|
+
export async function checkTaskReadmeMigrationState(repoRoot, ctx) {
|
|
142
143
|
const projectionTasks = await readTaskDocSnapshotsFromProjection(ctx);
|
|
143
144
|
const tasks = projectionTasks && projectionTasks.length > 0
|
|
144
145
|
? projectionTasks
|
|
@@ -197,6 +198,129 @@ async function checkDoneTaskReadmeArchiveDrift(repoRoot, ctx) {
|
|
|
197
198
|
}),
|
|
198
199
|
];
|
|
199
200
|
}
|
|
201
|
+
function normalizeTaskBodyForComparison(text) {
|
|
202
|
+
return text.replaceAll("\r\n", "\n").trim();
|
|
203
|
+
}
|
|
204
|
+
function normalizeCanonicalSections(value) {
|
|
205
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
206
|
+
return null;
|
|
207
|
+
const sections = {};
|
|
208
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
209
|
+
const title = key.trim();
|
|
210
|
+
if (!title || typeof entry !== "string")
|
|
211
|
+
continue;
|
|
212
|
+
sections[title] = entry;
|
|
213
|
+
}
|
|
214
|
+
return Object.keys(sections).length > 0 ? sections : null;
|
|
215
|
+
}
|
|
216
|
+
async function checkTaskProjectionDrift(repoRoot, ctx) {
|
|
217
|
+
const workflowDir = path.join(repoRoot, ctx?.config.paths.workflow_dir ?? ".agentplane/tasks");
|
|
218
|
+
let entries;
|
|
219
|
+
try {
|
|
220
|
+
entries = await fs.readdir(workflowDir, { withFileTypes: true });
|
|
221
|
+
}
|
|
222
|
+
catch {
|
|
223
|
+
return [];
|
|
224
|
+
}
|
|
225
|
+
const drifted = [];
|
|
226
|
+
for (const entry of entries) {
|
|
227
|
+
if (!entry.isDirectory())
|
|
228
|
+
continue;
|
|
229
|
+
const readmePath = path.join(workflowDir, entry.name, "README.md");
|
|
230
|
+
let text = "";
|
|
231
|
+
try {
|
|
232
|
+
text = await fs.readFile(readmePath, "utf8");
|
|
233
|
+
}
|
|
234
|
+
catch {
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
let parsed;
|
|
238
|
+
try {
|
|
239
|
+
parsed = parseTaskReadme(text);
|
|
240
|
+
}
|
|
241
|
+
catch {
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
const sections = normalizeCanonicalSections(parsed.frontmatter.sections);
|
|
245
|
+
if (!sections)
|
|
246
|
+
continue;
|
|
247
|
+
const renderedBody = renderTaskDocFromSections(sections);
|
|
248
|
+
if (normalizeTaskBodyForComparison(parsed.body) === normalizeTaskBodyForComparison(renderedBody)) {
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
const taskId = typeof parsed.frontmatter.id === "string" && parsed.frontmatter.id.trim()
|
|
252
|
+
? parsed.frontmatter.id.trim()
|
|
253
|
+
: entry.name;
|
|
254
|
+
const status = typeof parsed.frontmatter.status === "string" && parsed.frontmatter.status.trim()
|
|
255
|
+
? parsed.frontmatter.status.trim().toUpperCase()
|
|
256
|
+
: "UNKNOWN";
|
|
257
|
+
drifted.push({ id: taskId, status });
|
|
258
|
+
}
|
|
259
|
+
if (drifted.length === 0)
|
|
260
|
+
return [];
|
|
261
|
+
const activeCount = drifted.filter((task) => task.status !== "DONE").length;
|
|
262
|
+
const examples = drifted
|
|
263
|
+
.slice(0, 5)
|
|
264
|
+
.map((task) => `${task.id}[${task.status}]`)
|
|
265
|
+
.join(", ");
|
|
266
|
+
return [
|
|
267
|
+
renderDiagnosticFinding({
|
|
268
|
+
severity: "WARN",
|
|
269
|
+
state: "task README projection drift detected",
|
|
270
|
+
likelyCause: "canonical frontmatter.sections no longer match the rendered task body on disk",
|
|
271
|
+
nextAction: {
|
|
272
|
+
command: "agentplane task normalize",
|
|
273
|
+
reason: "re-render task README bodies from canonical one-file task state",
|
|
274
|
+
},
|
|
275
|
+
details: [
|
|
276
|
+
`Drifted task READMEs: ${drifted.length}; active drifted tasks: ${activeCount}`,
|
|
277
|
+
examples ? `Examples: ${examples}` : "Examples unavailable.",
|
|
278
|
+
],
|
|
279
|
+
}),
|
|
280
|
+
];
|
|
281
|
+
}
|
|
282
|
+
function checkBackendReadiness(ctx) {
|
|
283
|
+
if (ctx?.backendId !== "redmine")
|
|
284
|
+
return [];
|
|
285
|
+
const { supports_task_revisions, supports_revision_guarded_writes } = ctx.taskBackend.capabilities;
|
|
286
|
+
if (supports_task_revisions === supports_revision_guarded_writes &&
|
|
287
|
+
supports_task_revisions === true) {
|
|
288
|
+
return [];
|
|
289
|
+
}
|
|
290
|
+
if (supports_task_revisions === false && supports_revision_guarded_writes === false) {
|
|
291
|
+
return [
|
|
292
|
+
renderDiagnosticFinding({
|
|
293
|
+
severity: "WARN",
|
|
294
|
+
state: "Redmine backend is running in partial compatibility mode without canonical_state support",
|
|
295
|
+
likelyCause: "AGENTPLANE_REDMINE_CUSTOM_FIELDS_CANONICAL_STATE is not configured, so Redmine cannot round-trip the full canonical task state or guard writes by remote revision",
|
|
296
|
+
nextAction: {
|
|
297
|
+
command: "agentplane backend inspect redmine --yes",
|
|
298
|
+
reason: "inspect visible Redmine custom fields first, then wire AGENTPLANE_REDMINE_CUSTOM_FIELDS_CANONICAL_STATE to the correct field id",
|
|
299
|
+
},
|
|
300
|
+
details: [
|
|
301
|
+
`Backend config: ${ctx.backendConfigPath}`,
|
|
302
|
+
"Current capability flags: supports_task_revisions=false; supports_revision_guarded_writes=false",
|
|
303
|
+
"Legacy doc field syncing can still work, but the backend remains partial-compatibility only.",
|
|
304
|
+
],
|
|
305
|
+
}),
|
|
306
|
+
];
|
|
307
|
+
}
|
|
308
|
+
return [
|
|
309
|
+
renderDiagnosticFinding({
|
|
310
|
+
severity: "WARN",
|
|
311
|
+
state: "Redmine backend capability contract is internally inconsistent",
|
|
312
|
+
likelyCause: "backend capability flags diverged, so doctor cannot rely on a single revision-guard readiness state",
|
|
313
|
+
nextAction: {
|
|
314
|
+
command: "inspect Redmine backend capability wiring and rerun agentplane doctor",
|
|
315
|
+
reason: "restore a coherent readiness contract before relying on guarded remote writes",
|
|
316
|
+
},
|
|
317
|
+
details: [
|
|
318
|
+
`Backend config: ${ctx.backendConfigPath}`,
|
|
319
|
+
`Current capability flags: supports_task_revisions=${String(supports_task_revisions)}; supports_revision_guarded_writes=${String(supports_revision_guarded_writes)}`,
|
|
320
|
+
],
|
|
321
|
+
}),
|
|
322
|
+
];
|
|
323
|
+
}
|
|
200
324
|
export async function checkWorkspace(repoRoot, opts) {
|
|
201
325
|
const problems = [];
|
|
202
326
|
const requiredFiles = [path.join(repoRoot, ".agentplane", "config.json")];
|
|
@@ -243,6 +367,6 @@ export async function checkWorkspace(repoRoot, opts) {
|
|
|
243
367
|
if (!hasJson) {
|
|
244
368
|
problems.push("No agent profiles found in .agentplane/agents (*.json expected).");
|
|
245
369
|
}
|
|
246
|
-
problems.push(...(await checkTaskReadmeMigrationState(repoRoot, opts?.ctx)), ...(await checkDoneTaskReadmeArchiveDrift(repoRoot, opts?.ctx)));
|
|
370
|
+
problems.push(...checkBackendReadiness(opts?.ctx), ...(await checkTaskReadmeMigrationState(repoRoot, opts?.ctx)), ...(await checkDoneTaskReadmeArchiveDrift(repoRoot, opts?.ctx)), ...(await checkTaskProjectionDrift(repoRoot, opts?.ctx)));
|
|
247
371
|
return problems;
|
|
248
372
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commit.command.d.ts","sourceRoot":"","sources":["../../../src/commands/guard/commit.command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAOtE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAIhE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,OAAO,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,WAAW,CAAC,iBAAiB,
|
|
1
|
+
{"version":3,"file":"commit.command.d.ts","sourceRoot":"","sources":["../../../src/commands/guard/commit.command.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAOtE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAIhE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,OAAO,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,WAAW,CAAC,iBAAiB,CAmI1D,CAAC;AAEF,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,IAC1E,KAAK,UAAU,EAAE,GAAG,iBAAiB,KAAG,OAAO,CAAC,MAAM,CAAC,CA2BtE"}
|
|
@@ -34,23 +34,38 @@ export const guardCommitSpec = {
|
|
|
34
34
|
kind: "boolean",
|
|
35
35
|
name: "allow-tasks",
|
|
36
36
|
default: false,
|
|
37
|
-
description: "Allow
|
|
37
|
+
description: "Allow the tasks export snapshot plus artifacts under the active task subtree; standalone path scope.",
|
|
38
38
|
},
|
|
39
39
|
{
|
|
40
40
|
kind: "boolean",
|
|
41
41
|
name: "allow-base",
|
|
42
42
|
default: false,
|
|
43
|
-
description: "Allow base branch edits.",
|
|
43
|
+
description: "Allow base branch edits; branch override only, not a path allowlist.",
|
|
44
44
|
},
|
|
45
45
|
{
|
|
46
46
|
kind: "boolean",
|
|
47
47
|
name: "allow-policy",
|
|
48
48
|
default: false,
|
|
49
|
-
description: "Allow policy edits (e.g. AGENTS.md).",
|
|
49
|
+
description: "Allow policy edits (e.g. AGENTS.md); standalone path scope.",
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
kind: "boolean",
|
|
53
|
+
name: "allow-config",
|
|
54
|
+
default: false,
|
|
55
|
+
description: "Allow config edits; standalone path scope.",
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
kind: "boolean",
|
|
59
|
+
name: "allow-hooks",
|
|
60
|
+
default: false,
|
|
61
|
+
description: "Allow hooks edits; standalone path scope.",
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
kind: "boolean",
|
|
65
|
+
name: "allow-ci",
|
|
66
|
+
default: false,
|
|
67
|
+
description: "Allow CI workflow edits; standalone path scope.",
|
|
50
68
|
},
|
|
51
|
-
{ kind: "boolean", name: "allow-config", default: false, description: "Allow config edits." },
|
|
52
|
-
{ kind: "boolean", name: "allow-hooks", default: false, description: "Allow hooks edits." },
|
|
53
|
-
{ kind: "boolean", name: "allow-ci", default: false, description: "Allow CI workflow edits." },
|
|
54
69
|
{
|
|
55
70
|
kind: "boolean",
|
|
56
71
|
name: "require-clean",
|
|
@@ -64,6 +79,15 @@ export const guardCommitSpec = {
|
|
|
64
79
|
cmd: 'agentplane guard commit 202602030608-F1Q8AB -m "✨ F1Q8AB task: implement allowlist guard" --allow packages/agentplane',
|
|
65
80
|
why: "Validate staged changes are covered by allowlist and policy.",
|
|
66
81
|
},
|
|
82
|
+
{
|
|
83
|
+
cmd: 'agentplane guard commit 202602030608-F1Q8AB -m "✨ F1Q8AB task: update publish workflow" --allow-ci',
|
|
84
|
+
why: "Validate already staged CI-only changes without a redundant explicit workflow path prefix.",
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
notes: [
|
|
88
|
+
"Protected path-scoped overrides can stand alone without a duplicate explicit prefix: `--allow-tasks`, `--allow-policy`, `--allow-config`, `--allow-hooks`, and `--allow-ci` each admit their own path family.",
|
|
89
|
+
"`agentplane guard commit` remains staged-only: it validates the current index and never auto-stages files for you.",
|
|
90
|
+
"`--allow-base` is different: it only overrides base-branch protection and never selects file paths by itself.",
|
|
67
91
|
],
|
|
68
92
|
validateRaw: (raw) => {
|
|
69
93
|
const msg = typeof raw.opts.message === "string" ? raw.opts.message.trim() : "";
|
|
@@ -13,6 +13,15 @@ export declare function stageAllowlist(opts: {
|
|
|
13
13
|
ctx: CommandContext;
|
|
14
14
|
allow: string[];
|
|
15
15
|
allowTasks: boolean;
|
|
16
|
+
allowPolicy?: boolean;
|
|
17
|
+
allowConfig?: boolean;
|
|
18
|
+
allowHooks?: boolean;
|
|
19
|
+
allowCI?: boolean;
|
|
16
20
|
tasksPath: string;
|
|
21
|
+
workflowDir?: string;
|
|
22
|
+
taskId?: string;
|
|
23
|
+
allowTaskOnly?: boolean;
|
|
24
|
+
emptyAllowMessage?: string;
|
|
25
|
+
noMatchMessage?: string;
|
|
17
26
|
}): Promise<string[]>;
|
|
18
27
|
//# sourceMappingURL=allow.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"allow.d.ts","sourceRoot":"","sources":["../../../../src/commands/guard/impl/allow.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"allow.d.ts","sourceRoot":"","sources":["../../../../src/commands/guard/impl/allow.ts"],"names":[],"mappings":"AAUA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAkBvF,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAe9D;AAED,wBAAsB,qBAAqB,CAAC,IAAI,EAAE;IAChD,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAOpB;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE;IACzC,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqBhB;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE;IACzC,GAAG,EAAE,cAAc,CAAC;IACpB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,UAAU,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAoEpB"}
|
|
@@ -2,6 +2,7 @@ import { resolveProject } from "@agentplaneorg/core";
|
|
|
2
2
|
import { exitCodeForError } from "../../../cli/exit-codes.js";
|
|
3
3
|
import { gitPathIsUnderPrefix, normalizeGitPathPrefix } from "../../../shared/git-path.js";
|
|
4
4
|
import { CliError } from "../../../shared/errors.js";
|
|
5
|
+
import { protectedPathAllowPrefixes, taskArtifactPrefixes, } from "../../../shared/protected-paths.js";
|
|
5
6
|
import { GitContext } from "../../shared/git-context.js";
|
|
6
7
|
import { loadCommandContext } from "../../shared/task-backend.js";
|
|
7
8
|
function normalizeAllowPrefixes(prefixes) {
|
|
@@ -75,28 +76,43 @@ export async function stageAllowlist(opts) {
|
|
|
75
76
|
});
|
|
76
77
|
}
|
|
77
78
|
const allow = normalizeAllowPrefixes(opts.allow);
|
|
78
|
-
if (allow.
|
|
79
|
+
if (allow.includes(".")) {
|
|
79
80
|
throw new CliError({
|
|
80
81
|
exitCode: 2,
|
|
81
82
|
code: "E_USAGE",
|
|
82
|
-
message: "
|
|
83
|
+
message: "Repo-wide allowlist ('.') is not allowed; choose minimal prefixes (tip: `agentplane guard suggest-allow --format args`).",
|
|
83
84
|
});
|
|
84
85
|
}
|
|
85
|
-
|
|
86
|
+
const taskAllow = taskArtifactPrefixes({
|
|
87
|
+
tasksPath: opts.tasksPath,
|
|
88
|
+
workflowDir: opts.workflowDir,
|
|
89
|
+
taskId: opts.taskId,
|
|
90
|
+
});
|
|
91
|
+
const protectedAllow = protectedPathAllowPrefixes({
|
|
92
|
+
tasksPath: opts.tasksPath,
|
|
93
|
+
workflowDir: opts.workflowDir,
|
|
94
|
+
taskId: opts.taskId,
|
|
95
|
+
allowTasks: opts.allowTasks,
|
|
96
|
+
allowPolicy: opts.allowPolicy,
|
|
97
|
+
allowConfig: opts.allowConfig,
|
|
98
|
+
allowHooks: opts.allowHooks,
|
|
99
|
+
allowCI: opts.allowCI,
|
|
100
|
+
});
|
|
101
|
+
const effectiveAllow = normalizeAllowPrefixes([...allow, ...protectedAllow]);
|
|
102
|
+
if (effectiveAllow.length === 0 ||
|
|
103
|
+
(allow.length === 0 && protectedAllow.length === 0 && opts.allowTaskOnly !== true)) {
|
|
86
104
|
throw new CliError({
|
|
87
105
|
exitCode: 2,
|
|
88
106
|
code: "E_USAGE",
|
|
89
|
-
message:
|
|
107
|
+
message: opts.emptyAllowMessage ?? "Provide at least one allowed prefix",
|
|
90
108
|
});
|
|
91
109
|
}
|
|
92
|
-
const denied =
|
|
93
|
-
if (!opts.allowTasks)
|
|
94
|
-
denied.add(opts.tasksPath);
|
|
110
|
+
const denied = opts.allowTasks ? [] : taskAllow;
|
|
95
111
|
const staged = [];
|
|
96
112
|
for (const filePath of changed) {
|
|
97
|
-
if (denied.
|
|
113
|
+
if (denied.some((prefix) => gitPathIsUnderPrefix(filePath, prefix)))
|
|
98
114
|
continue;
|
|
99
|
-
if (
|
|
115
|
+
if (effectiveAllow.some((prefix) => gitPathIsUnderPrefix(filePath, prefix))) {
|
|
100
116
|
staged.push(filePath);
|
|
101
117
|
}
|
|
102
118
|
}
|
|
@@ -105,7 +121,7 @@ export async function stageAllowlist(opts) {
|
|
|
105
121
|
throw new CliError({
|
|
106
122
|
exitCode: 2,
|
|
107
123
|
code: "E_USAGE",
|
|
108
|
-
message: "No changes matched allowed prefixes (update --commit-allow)",
|
|
124
|
+
message: opts.noMatchMessage ?? "No changes matched allowed prefixes (update --commit-allow)",
|
|
109
125
|
});
|
|
110
126
|
}
|
|
111
127
|
// `git add <pathspec>` is not reliable for staging deletes/renames across versions/configs.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../../../src/commands/guard/impl/commands.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../../../src/commands/guard/impl/commands.ts"],"names":[],"mappings":"AAKA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAQvF,OAAO,EAAoB,KAAK,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAuMxE,wBAAsB,aAAa,CAAC,IAAI,EAAE;IACxC,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;CAChB,GAAG,OAAO,CAAC,MAAM,CAAC,CAsBlB;AAED,wBAAsB,oBAAoB,CAAC,IAAI,EAAE;IAC/C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC;CAC1B,GAAG,OAAO,CAAC,MAAM,CAAC,CAyBlB;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,CAa9E;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE;IACpC,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,OAAO,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,OAAO,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;IACf,kBAAkB,EAAE,OAAO,CAAC;IAC5B,cAAc,EAAE,OAAO,CAAC;CACzB,GAAG,OAAO,CAAC,MAAM,CAAC,CAuMlB"}
|
|
@@ -1,15 +1,30 @@
|
|
|
1
1
|
import { mapCoreError } from "../../../cli/error-map.js";
|
|
2
|
-
import { successMessage } from "../../../cli/output.js";
|
|
2
|
+
import { infoMessage, successMessage } from "../../../cli/output.js";
|
|
3
|
+
import { stripAnsi } from "../../../cli/shared/ansi.js";
|
|
3
4
|
import { withDiagnosticContext } from "../../../shared/diagnostics.js";
|
|
4
5
|
import { CliError } from "../../../shared/errors.js";
|
|
5
6
|
import { loadCommandContext } from "../../shared/task-backend.js";
|
|
6
7
|
import { loadTaskFromContext } from "../../shared/task-backend.js";
|
|
7
8
|
import { execFileAsync, gitEnv } from "../../shared/git.js";
|
|
8
9
|
import { ensureReconciledBeforeMutation } from "../../shared/reconcile-check.js";
|
|
9
|
-
import { suggestAllowPrefixes } from "./allow.js";
|
|
10
|
+
import { stageAllowlist, suggestAllowPrefixes } from "./allow.js";
|
|
10
11
|
import { buildCloseCommitMessage, taskReadmePathForTask } from "./close-message.js";
|
|
11
12
|
import { buildGitCommitEnv } from "./env.js";
|
|
12
13
|
import { guardCommitCheck } from "./policy.js";
|
|
14
|
+
const COMMIT_FAILURE_SIGNAL_PATTERNS = [
|
|
15
|
+
/Code style issues found/i,
|
|
16
|
+
/Run Prettier with --write/i,
|
|
17
|
+
/\bESLint\b/i,
|
|
18
|
+
/\b[0-9]+\s+problems?\b/i,
|
|
19
|
+
/\berror\b/i,
|
|
20
|
+
/\bfailed\b/i,
|
|
21
|
+
/✖/,
|
|
22
|
+
];
|
|
23
|
+
const FORMATTER_SIGNAL_PATTERNS = [
|
|
24
|
+
/Code style issues found/i,
|
|
25
|
+
/Run Prettier with --write/i,
|
|
26
|
+
];
|
|
27
|
+
const ESLINT_SIGNAL_PATTERNS = [/\bESLint\b/i, /\b[0-9]+\s+problems?\b/i];
|
|
13
28
|
function readText(value) {
|
|
14
29
|
if (typeof value === "string")
|
|
15
30
|
return value;
|
|
@@ -21,16 +36,108 @@ function summarizeOutput(raw) {
|
|
|
21
36
|
const lines = raw
|
|
22
37
|
.replaceAll("\r\n", "\n")
|
|
23
38
|
.split("\n")
|
|
24
|
-
.map((line) => line.trimEnd())
|
|
39
|
+
.map((line) => stripAnsi(line).trimEnd())
|
|
25
40
|
.filter((line) => line.trim().length > 0)
|
|
26
41
|
.map((line) => (line.length > 180 ? `${line.slice(0, 180)} [truncated]` : line));
|
|
27
42
|
if (lines.length <= 12)
|
|
28
43
|
return lines;
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
44
|
+
const selected = new Set();
|
|
45
|
+
for (let index = 0; index < Math.min(6, lines.length); index += 1) {
|
|
46
|
+
selected.add(index);
|
|
47
|
+
}
|
|
48
|
+
for (let index = Math.max(lines.length - 6, 0); index < lines.length; index += 1) {
|
|
49
|
+
selected.add(index);
|
|
50
|
+
}
|
|
51
|
+
for (const [index, line] of lines.entries()) {
|
|
52
|
+
if (selected.has(index))
|
|
53
|
+
continue;
|
|
54
|
+
if (COMMIT_FAILURE_SIGNAL_PATTERNS.some((pattern) => pattern.test(line))) {
|
|
55
|
+
selected.add(index);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const ordered = [...selected].toSorted((a, b) => a - b);
|
|
59
|
+
const summary = [];
|
|
60
|
+
let previous = -1;
|
|
61
|
+
for (const index of ordered) {
|
|
62
|
+
if (previous >= 0 && index - previous > 1) {
|
|
63
|
+
summary.push(`[${index - previous - 1} lines omitted]`);
|
|
64
|
+
}
|
|
65
|
+
summary.push(lines[index] ?? "");
|
|
66
|
+
previous = index;
|
|
67
|
+
}
|
|
68
|
+
return summary;
|
|
69
|
+
}
|
|
70
|
+
function detectCommitFailureSignal(output) {
|
|
71
|
+
if (FORMATTER_SIGNAL_PATTERNS.some((pattern) => pattern.test(output))) {
|
|
72
|
+
return "formatter";
|
|
73
|
+
}
|
|
74
|
+
if (ESLINT_SIGNAL_PATTERNS.some((pattern) => pattern.test(output))) {
|
|
75
|
+
return "eslint";
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
function commitFailureDiagnostic(phase, output) {
|
|
80
|
+
const signal = detectCommitFailureSignal(output);
|
|
81
|
+
if (signal === "formatter") {
|
|
82
|
+
return {
|
|
83
|
+
state: phase === "close_commit"
|
|
84
|
+
? "git rejected the generated close commit"
|
|
85
|
+
: "git rejected the requested task-scoped commit",
|
|
86
|
+
likelyCause: phase === "close_commit"
|
|
87
|
+
? "a formatting check in the pre-commit path rejected the deterministic close commit after the task README was staged"
|
|
88
|
+
: "a formatting check in the pre-commit path rejected the staged task-scoped commit",
|
|
89
|
+
nextAction: {
|
|
90
|
+
command: "bun run format",
|
|
91
|
+
reason: "apply formatter fixes before retrying the commit flow",
|
|
92
|
+
reasonCode: "git_pre_commit_format",
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
if (signal === "eslint") {
|
|
97
|
+
return {
|
|
98
|
+
state: phase === "close_commit"
|
|
99
|
+
? "git rejected the generated close commit"
|
|
100
|
+
: "git rejected the requested task-scoped commit",
|
|
101
|
+
likelyCause: phase === "close_commit"
|
|
102
|
+
? "a lint check in the pre-commit path rejected the deterministic close commit after the task README was staged"
|
|
103
|
+
: "a lint check in the pre-commit path rejected the staged task-scoped commit",
|
|
104
|
+
nextAction: {
|
|
105
|
+
command: "bun run lint:core",
|
|
106
|
+
reason: "rerun lint and fix the reported error before retrying the commit flow",
|
|
107
|
+
reasonCode: "git_pre_commit_lint",
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
if (phase === "close_commit") {
|
|
112
|
+
return {
|
|
113
|
+
state: "git rejected the generated close commit",
|
|
114
|
+
likelyCause: "a hook or commit policy blocked the deterministic task close commit after the task README was staged",
|
|
115
|
+
nextAction: {
|
|
116
|
+
command: "git status --short --untracked-files=no",
|
|
117
|
+
reason: "inspect the staged close-commit payload before fixing the hook or policy failure",
|
|
118
|
+
reasonCode: "git_close_commit_blocked",
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
state: "git rejected the requested task-scoped commit",
|
|
124
|
+
likelyCause: "a hook or commit policy blocked the staged changes after guard validation passed",
|
|
125
|
+
nextAction: {
|
|
126
|
+
command: "git status --short --untracked-files=no",
|
|
127
|
+
reason: "inspect the staged task-scoped payload before fixing the hook or policy failure",
|
|
128
|
+
reasonCode: "git_task_commit_blocked",
|
|
129
|
+
},
|
|
130
|
+
};
|
|
32
131
|
}
|
|
33
|
-
function
|
|
132
|
+
function hasExplicitCommitScope(opts) {
|
|
133
|
+
return (opts.allow.some((prefix) => prefix.trim().length > 0) ||
|
|
134
|
+
opts.allowTasks ||
|
|
135
|
+
opts.allowPolicy ||
|
|
136
|
+
opts.allowConfig ||
|
|
137
|
+
opts.allowHooks ||
|
|
138
|
+
opts.allowCI);
|
|
139
|
+
}
|
|
140
|
+
function asCommitFailure(err, phase) {
|
|
34
141
|
if (err instanceof Error) {
|
|
35
142
|
const e = err;
|
|
36
143
|
const cmd = typeof e.cmd === "string" ? e.cmd : "";
|
|
@@ -51,15 +158,7 @@ function asCommitFailure(err) {
|
|
|
51
158
|
exitCode: 5,
|
|
52
159
|
code: "E_GIT",
|
|
53
160
|
message: lines.join("\n"),
|
|
54
|
-
context: withDiagnosticContext({ command: "commit" },
|
|
55
|
-
state: "git rejected the generated close commit",
|
|
56
|
-
likelyCause: "a hook or commit policy blocked the deterministic task close commit after the task README was staged",
|
|
57
|
-
nextAction: {
|
|
58
|
-
command: "git status --short --untracked-files=no",
|
|
59
|
-
reason: "inspect the staged close-commit payload before fixing the hook or policy failure",
|
|
60
|
-
reasonCode: "git_close_commit_blocked",
|
|
61
|
-
},
|
|
62
|
-
}),
|
|
161
|
+
context: withDiagnosticContext({ command: "commit" }, commitFailureDiagnostic(phase, output)),
|
|
63
162
|
});
|
|
64
163
|
}
|
|
65
164
|
return null;
|
|
@@ -235,6 +334,35 @@ export async function cmdCommit(opts) {
|
|
|
235
334
|
});
|
|
236
335
|
}
|
|
237
336
|
await ensureReconciledBeforeMutation({ ctx, command: "commit" });
|
|
337
|
+
let autoStaged = [];
|
|
338
|
+
const staged = await ctx.git.statusStagedPaths();
|
|
339
|
+
if (staged.length === 0) {
|
|
340
|
+
if (!hasExplicitCommitScope(opts)) {
|
|
341
|
+
throw new CliError({
|
|
342
|
+
exitCode: 2,
|
|
343
|
+
code: "E_USAGE",
|
|
344
|
+
message: "No staged files and no commit allowlist. Pass --allow <path-prefix>, use --allow-tasks for active task artifacts, or stage files manually.",
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
autoStaged = await stageAllowlist({
|
|
348
|
+
ctx,
|
|
349
|
+
allow: opts.allow,
|
|
350
|
+
allowTasks: opts.allowTasks,
|
|
351
|
+
allowPolicy: opts.allowPolicy,
|
|
352
|
+
allowConfig: opts.allowConfig,
|
|
353
|
+
allowHooks: opts.allowHooks,
|
|
354
|
+
allowCI: opts.allowCI,
|
|
355
|
+
tasksPath: ctx.config.paths.tasks_path,
|
|
356
|
+
workflowDir: ctx.config.paths.workflow_dir,
|
|
357
|
+
taskId: opts.taskId,
|
|
358
|
+
allowTaskOnly: true,
|
|
359
|
+
emptyAllowMessage: "No staged files and no commit allowlist. Pass --allow <path-prefix>, use --allow-tasks for active task artifacts, or stage files manually.",
|
|
360
|
+
noMatchMessage: "No changed files matched the commit allowlist (adjust --allow, protected allow flags, or --allow-tasks; otherwise stage files manually).",
|
|
361
|
+
});
|
|
362
|
+
if (!opts.quiet) {
|
|
363
|
+
process.stdout.write(`${infoMessage(`commit auto-staged ${autoStaged.length} path(s) from allowlist`)}\n`);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
238
366
|
await guardCommitCheck({
|
|
239
367
|
ctx,
|
|
240
368
|
cwd: opts.cwd,
|
|
@@ -263,14 +391,14 @@ export async function cmdCommit(opts) {
|
|
|
263
391
|
await ctx.git.commit({ message: opts.message, env });
|
|
264
392
|
if (!opts.quiet) {
|
|
265
393
|
const { hash, subject } = await ctx.git.headHashSubject();
|
|
266
|
-
process.stdout.write(`${successMessage("committed", `${hash?.slice(0, 12) ?? ""} ${subject ?? ""}`.trim())}\n`);
|
|
394
|
+
process.stdout.write(`${successMessage("committed", `${hash?.slice(0, 12) ?? ""} ${subject ?? ""}`.trim(), autoStaged.length > 0 ? `staged=${autoStaged.join(", ")}` : undefined)}\n`);
|
|
267
395
|
}
|
|
268
396
|
return 0;
|
|
269
397
|
}
|
|
270
398
|
catch (err) {
|
|
271
399
|
if (err instanceof CliError)
|
|
272
400
|
throw err;
|
|
273
|
-
const commitFailure = asCommitFailure(err);
|
|
401
|
+
const commitFailure = asCommitFailure(err, opts.close ? "close_commit" : "task_commit");
|
|
274
402
|
if (commitFailure)
|
|
275
403
|
throw commitFailure;
|
|
276
404
|
throw mapCoreError(err, { command: "commit", root: opts.rootOverride ?? null });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"comment-commit.d.ts","sourceRoot":"","sources":["../../../../src/commands/guard/impl/comment-commit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAQ/E,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAiEvF,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,gBAAgB,CAAC;CAC1B,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"comment-commit.d.ts","sourceRoot":"","sources":["../../../../src/commands/guard/impl/comment-commit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAQ/E,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAiEvF,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,GAAG,CAAC,EAAE,cAAc,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,gBAAgB,CAAC;CAC1B,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CA2F/D"}
|
|
@@ -68,6 +68,8 @@ export async function commitFromComment(opts) {
|
|
|
68
68
|
allow: allowPrefixes,
|
|
69
69
|
allowTasks: opts.allowTasks,
|
|
70
70
|
tasksPath: opts.config.paths.tasks_path,
|
|
71
|
+
workflowDir: opts.config.paths.workflow_dir,
|
|
72
|
+
taskId: opts.taskId,
|
|
71
73
|
});
|
|
72
74
|
const message = deriveCommitMessageFromComment({
|
|
73
75
|
taskId: opts.taskId,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/hooks/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/commands/hooks/index.ts"],"names":[],"mappings":"AAiBA,eAAO,MAAM,UAAU,mDAAoD,CAAC;AA8G5E,wBAAsB,eAAe,CAAC,IAAI,EAAE;IAC1C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;CAChB,GAAG,OAAO,CAAC,MAAM,CAAC,CAmClB;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;CAChB,GAAG,OAAO,CAAC,MAAM,CAAC,CA4BlB;AAED,wBAAsB,WAAW,CAAC,IAAI,EAAE;IACtC,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC;IAClC,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB,GAAG,OAAO,CAAC,MAAM,CAAC,CAsGlB"}
|
|
@@ -5,13 +5,11 @@ import { evaluatePolicy } from "../../policy/evaluate.js";
|
|
|
5
5
|
import { mapBackendError, mapCoreError } from "../../cli/error-map.js";
|
|
6
6
|
import { fileExists } from "../../cli/fs-utils.js";
|
|
7
7
|
import { infoMessage, successMessage } from "../../cli/output.js";
|
|
8
|
-
import { resolveCommitEmojiForAgent } from "../../shared/agent-emoji.js";
|
|
9
8
|
import { CliError } from "../../shared/errors.js";
|
|
10
9
|
import { GitContext } from "../shared/git-context.js";
|
|
11
10
|
import { throwIfPolicyDenied } from "../shared/policy-deny.js";
|
|
12
11
|
import { gitCurrentBranch, gitRevParse } from "../shared/git-ops.js";
|
|
13
12
|
import { isPathWithin } from "../shared/path.js";
|
|
14
|
-
import { readDirectWorkLock } from "../../shared/direct-work-lock.js";
|
|
15
13
|
const HOOK_MARKER = "agentplane-hook";
|
|
16
14
|
const SHIM_MARKER = "agentplane-hook-shim";
|
|
17
15
|
export const HOOK_NAMES = ["commit-msg", "pre-commit", "pre-push"];
|
|
@@ -201,40 +199,15 @@ export async function cmdHooksRun(opts) {
|
|
|
201
199
|
const loaded = await loadConfig(resolved.agentplaneDir);
|
|
202
200
|
const taskId = (process.env.AGENTPLANE_TASK_ID ?? "").trim();
|
|
203
201
|
const statusTo = (process.env.AGENTPLANE_STATUS_TO ?? "").trim().toUpperCase();
|
|
204
|
-
const agentsDirAbs = path.join(resolved.gitRoot, loaded.config.paths.agents_dir);
|
|
205
|
-
let agentId = (process.env.AGENTPLANE_AGENT_ID ?? "").trim();
|
|
206
|
-
if (!agentId && loaded.config.workflow_mode === "direct" && taskId) {
|
|
207
|
-
const lock = await readDirectWorkLock(resolved.agentplaneDir);
|
|
208
|
-
const lockAgent = lock?.agent?.trim() ?? "";
|
|
209
|
-
if (lock?.task_id === taskId && lockAgent)
|
|
210
|
-
agentId = lockAgent;
|
|
211
|
-
}
|
|
212
202
|
const emoji = subject.split(/\s+/).find(Boolean) ?? "";
|
|
213
|
-
if (taskId) {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
" ✅ <TASK_SUFFIX> <scope>: <summary>",
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
else if (agentId) {
|
|
226
|
-
const expectedEmoji = await resolveCommitEmojiForAgent({ agentsDirAbs, agentId });
|
|
227
|
-
if (emoji !== expectedEmoji) {
|
|
228
|
-
throw new CliError({
|
|
229
|
-
exitCode: 5,
|
|
230
|
-
code: "E_GIT",
|
|
231
|
-
message: "Commit emoji does not match the executor agent policy.\n" +
|
|
232
|
-
`executor_agent=${agentId}\n` +
|
|
233
|
-
"Expected:\n" +
|
|
234
|
-
` ${expectedEmoji} <TASK_SUFFIX> <scope>: <summary>`,
|
|
235
|
-
});
|
|
236
|
-
}
|
|
237
|
-
}
|
|
203
|
+
if (taskId && statusTo === "DONE" && emoji !== "✅") {
|
|
204
|
+
throw new CliError({
|
|
205
|
+
exitCode: 5,
|
|
206
|
+
code: "E_GIT",
|
|
207
|
+
message: "Finish commits must use a checkmark emoji.\n" +
|
|
208
|
+
"Expected:\n" +
|
|
209
|
+
" ✅ <TASK_SUFFIX> <scope>: <summary>",
|
|
210
|
+
});
|
|
238
211
|
}
|
|
239
212
|
const res = evaluatePolicy({
|
|
240
213
|
action: "hook_commit_msg",
|
|
@@ -3,6 +3,10 @@ export declare function moveRecipeDir(opts: {
|
|
|
3
3
|
from: string;
|
|
4
4
|
to: string;
|
|
5
5
|
}): Promise<void>;
|
|
6
|
+
export declare function validateRecipeAssets(opts: {
|
|
7
|
+
manifest: RecipeManifest;
|
|
8
|
+
recipeDir: string;
|
|
9
|
+
}): Promise<void>;
|
|
6
10
|
export declare function applyRecipeAgents(opts: {
|
|
7
11
|
manifest: RecipeManifest;
|
|
8
12
|
recipeDir: string;
|