agentplane 0.3.2 → 0.3.4
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/assets/AGENTS.md +4 -4
- package/assets/agents/CODER.json +14 -10
- package/assets/agents/CREATOR.json +8 -6
- package/assets/agents/DOCS.json +9 -6
- package/assets/agents/INTEGRATOR.json +9 -6
- package/assets/agents/ORCHESTRATOR.json +8 -6
- package/assets/agents/PLANNER.json +8 -5
- package/assets/agents/REDMINE.json +6 -3
- package/assets/agents/REVIEWER.json +8 -6
- package/assets/agents/TESTER.json +8 -4
- package/assets/agents/UPDATER.json +6 -4
- package/assets/agents/UPGRADER.json +7 -6
- package/assets/policy/dod.code.md +2 -2
- package/assets/policy/dod.core.md +16 -2
- package/assets/policy/dod.docs.md +2 -2
- package/assets/policy/incidents.md +44 -1
- package/assets/policy/workflow.direct.md +8 -4
- package/bin/agentplane.js +59 -9
- package/bin/dist-guard.js +78 -10
- package/bin/runtime-context.d.ts +3 -0
- package/bin/runtime-context.js +13 -0
- package/bin/runtime-watch.d.ts +26 -0
- package/bin/runtime-watch.js +116 -0
- package/bin/stale-dist-policy.d.ts +6 -0
- package/bin/stale-dist-policy.js +44 -0
- package/dist/.build-manifest.json +2485 -5
- package/dist/backends/task-backend/local-backend.d.ts.map +1 -1
- package/dist/backends/task-backend/local-backend.js +9 -12
- package/dist/backends/task-backend/redmine-backend.d.ts.map +1 -1
- package/dist/backends/task-backend/redmine-backend.js +23 -18
- package/dist/backends/task-backend/shared/constants.d.ts +1 -0
- package/dist/backends/task-backend/shared/constants.d.ts.map +1 -1
- package/dist/backends/task-backend/shared/constants.js +1 -0
- package/dist/backends/task-backend/shared/doc.d.ts +1 -0
- package/dist/backends/task-backend/shared/doc.d.ts.map +1 -1
- package/dist/backends/task-backend/shared/doc.js +4 -1
- package/dist/backends/task-backend/shared/export.js +3 -3
- package/dist/cli/bootstrap-guide.d.ts +2 -3
- package/dist/cli/bootstrap-guide.d.ts.map +1 -1
- package/dist/cli/bootstrap-guide.js +16 -35
- package/dist/cli/command-guide.d.ts +14 -1
- package/dist/cli/command-guide.d.ts.map +1 -1
- package/dist/cli/command-guide.js +71 -47
- package/dist/cli/run-cli/catalog.d.ts +7 -0
- package/dist/cli/run-cli/catalog.d.ts.map +1 -0
- package/dist/cli/run-cli/catalog.js +22 -0
- package/dist/cli/run-cli/command-catalog.d.ts +1 -1
- package/dist/cli/run-cli/command-catalog.d.ts.map +1 -1
- package/dist/cli/run-cli/command-catalog.js +11 -0
- package/dist/cli/run-cli/commands/core.d.ts.map +1 -1
- package/dist/cli/run-cli/commands/core.js +37 -29
- package/dist/cli/run-cli/commands/init/write-config.d.ts.map +1 -1
- package/dist/cli/run-cli/commands/init/write-config.js +2 -0
- package/dist/cli/run-cli/commands/init/write-workflow.d.ts.map +1 -1
- package/dist/cli/run-cli/commands/init/write-workflow.js +6 -55
- package/dist/cli/run-cli/commands/init.js +5 -14
- package/dist/cli/run-cli/error-guidance.d.ts +9 -0
- package/dist/cli/run-cli/error-guidance.d.ts.map +1 -0
- package/dist/cli/run-cli/error-guidance.js +180 -0
- package/dist/cli/run-cli/globals.d.ts +22 -0
- package/dist/cli/run-cli/globals.d.ts.map +1 -0
- package/dist/cli/run-cli/globals.js +197 -0
- package/dist/cli/run-cli/update-warning.d.ts +6 -0
- package/dist/cli/run-cli/update-warning.d.ts.map +1 -0
- package/dist/cli/run-cli/update-warning.js +64 -0
- package/dist/cli/run-cli.d.ts.map +1 -1
- package/dist/cli/run-cli.js +5 -476
- package/dist/cli/spec/docs-render.d.ts.map +1 -1
- package/dist/cli/spec/docs-render.js +14 -1
- package/dist/commands/doctor/archive.d.ts +4 -0
- package/dist/commands/doctor/archive.d.ts.map +1 -0
- package/dist/commands/doctor/archive.js +211 -0
- package/dist/commands/doctor/fixes.d.ts +9 -0
- package/dist/commands/doctor/fixes.d.ts.map +1 -0
- package/dist/commands/doctor/fixes.js +40 -0
- package/dist/commands/doctor/layering.d.ts +2 -0
- package/dist/commands/doctor/layering.d.ts.map +1 -0
- package/dist/commands/doctor/layering.js +87 -0
- package/dist/commands/doctor/runtime.d.ts +4 -0
- package/dist/commands/doctor/runtime.d.ts.map +1 -0
- package/dist/commands/doctor/runtime.js +56 -0
- package/dist/commands/doctor/workflow.d.ts +6 -0
- package/dist/commands/doctor/workflow.d.ts.map +1 -0
- package/dist/commands/doctor/workflow.js +62 -0
- package/dist/commands/doctor/workspace.d.ts +2 -0
- package/dist/commands/doctor/workspace.d.ts.map +1 -0
- package/dist/commands/doctor/workspace.js +165 -0
- package/dist/commands/doctor.run.d.ts.map +1 -1
- package/dist/commands/doctor.run.js +16 -342
- package/dist/commands/doctor.spec.d.ts +1 -0
- package/dist/commands/doctor.spec.d.ts.map +1 -1
- package/dist/commands/doctor.spec.js +15 -1
- package/dist/commands/guard/impl/commands.d.ts.map +1 -1
- package/dist/commands/guard/impl/commands.js +19 -0
- package/dist/commands/release/apply.command.d.ts +2 -8
- package/dist/commands/release/apply.command.d.ts.map +1 -1
- package/dist/commands/release/apply.command.js +158 -387
- package/dist/commands/release/apply.mutation.d.ts +7 -0
- package/dist/commands/release/apply.mutation.d.ts.map +1 -0
- package/dist/commands/release/apply.mutation.js +107 -0
- package/dist/commands/release/apply.preflight.d.ts +25 -0
- package/dist/commands/release/apply.preflight.d.ts.map +1 -0
- package/dist/commands/release/apply.preflight.js +338 -0
- package/dist/commands/release/apply.reporting.d.ts +4 -0
- package/dist/commands/release/apply.reporting.d.ts.map +1 -0
- package/dist/commands/release/apply.reporting.js +24 -0
- package/dist/commands/release/apply.types.d.ts +46 -0
- package/dist/commands/release/apply.types.d.ts.map +1 -0
- package/dist/commands/release/apply.types.js +1 -0
- package/dist/commands/runtime.command.d.ts +28 -0
- package/dist/commands/runtime.command.d.ts.map +1 -0
- package/dist/commands/runtime.command.js +169 -0
- package/dist/commands/shared/task-store.d.ts.map +1 -1
- package/dist/commands/shared/task-store.js +7 -3
- package/dist/commands/task/add.d.ts.map +1 -1
- package/dist/commands/task/add.js +3 -33
- package/dist/commands/task/block.d.ts.map +1 -1
- package/dist/commands/task/block.js +2 -2
- package/dist/commands/task/close-duplicate.d.ts.map +1 -1
- package/dist/commands/task/close-duplicate.js +2 -2
- package/dist/commands/task/close-noop.d.ts.map +1 -1
- package/dist/commands/task/close-noop.js +2 -2
- package/dist/commands/task/comment.js +2 -2
- package/dist/commands/task/derive.d.ts.map +1 -1
- package/dist/commands/task/derive.js +3 -3
- package/dist/commands/task/doc-template.d.ts +10 -0
- package/dist/commands/task/doc-template.d.ts.map +1 -0
- package/dist/commands/task/doc-template.js +104 -0
- package/dist/commands/task/doc.d.ts.map +1 -1
- package/dist/commands/task/doc.js +36 -1
- package/dist/commands/task/finish.d.ts.map +1 -1
- package/dist/commands/task/finish.js +7 -4
- package/dist/commands/task/migrate-doc.command.d.ts.map +1 -1
- package/dist/commands/task/migrate-doc.command.js +5 -1
- package/dist/commands/task/migrate-doc.d.ts.map +1 -1
- package/dist/commands/task/migrate-doc.js +136 -2
- package/dist/commands/task/new.d.ts.map +1 -1
- package/dist/commands/task/new.js +4 -110
- package/dist/commands/task/new.spec.js +3 -3
- package/dist/commands/task/plan.d.ts.map +1 -1
- package/dist/commands/task/plan.js +5 -4
- package/dist/commands/task/scaffold.d.ts.map +1 -1
- package/dist/commands/task/scaffold.js +7 -52
- package/dist/commands/task/set-status.d.ts.map +1 -1
- package/dist/commands/task/set-status.js +2 -2
- package/dist/commands/task/shared/dependencies.d.ts +15 -0
- package/dist/commands/task/shared/dependencies.d.ts.map +1 -0
- package/dist/commands/task/shared/dependencies.js +143 -0
- package/dist/commands/task/shared/docs.d.ts +21 -0
- package/dist/commands/task/shared/docs.d.ts.map +1 -0
- package/dist/commands/task/shared/docs.js +121 -0
- package/dist/commands/task/shared/listing.d.ts +20 -0
- package/dist/commands/task/shared/listing.d.ts.map +1 -0
- package/dist/commands/task/shared/listing.js +127 -0
- package/dist/commands/task/shared/tags.d.ts +24 -0
- package/dist/commands/task/shared/tags.d.ts.map +1 -0
- package/dist/commands/task/shared/tags.js +177 -0
- package/dist/commands/task/shared/transitions.d.ts +42 -0
- package/dist/commands/task/shared/transitions.d.ts.map +1 -0
- package/dist/commands/task/shared/transitions.js +175 -0
- package/dist/commands/task/shared.d.ts +5 -106
- package/dist/commands/task/shared.d.ts.map +1 -1
- package/dist/commands/task/shared.js +5 -681
- package/dist/commands/task/start.d.ts.map +1 -1
- package/dist/commands/task/start.js +7 -5
- package/dist/commands/task/verify-record.d.ts.map +1 -1
- package/dist/commands/task/verify-record.js +9 -25
- package/dist/commands/task/verify-show.command.d.ts.map +1 -1
- package/dist/commands/task/verify-show.command.js +5 -1
- package/dist/commands/upgrade/apply.d.ts +44 -0
- package/dist/commands/upgrade/apply.d.ts.map +1 -0
- package/dist/commands/upgrade/apply.js +180 -0
- package/dist/commands/upgrade/report.d.ts +21 -0
- package/dist/commands/upgrade/report.d.ts.map +1 -0
- package/dist/commands/upgrade/report.js +81 -0
- package/dist/commands/upgrade/source.d.ts +35 -0
- package/dist/commands/upgrade/source.d.ts.map +1 -0
- package/dist/commands/upgrade/source.js +109 -0
- package/dist/commands/upgrade/types.d.ts +31 -0
- package/dist/commands/upgrade/types.d.ts.map +1 -0
- package/dist/commands/upgrade/types.js +1 -0
- package/dist/commands/upgrade.d.ts +1 -35
- package/dist/commands/upgrade.d.ts.map +1 -1
- package/dist/commands/upgrade.js +68 -332
- package/dist/commands/workflow-build.command.d.ts.map +1 -1
- package/dist/commands/workflow-build.command.js +9 -15
- package/dist/shared/diagnostics.d.ts +23 -0
- package/dist/shared/diagnostics.d.ts.map +1 -0
- package/dist/shared/diagnostics.js +57 -0
- package/dist/shared/errors.d.ts +2 -0
- package/dist/shared/errors.d.ts.map +1 -1
- package/dist/shared/errors.js +2 -0
- package/dist/shared/repo-cli-version.d.ts +13 -0
- package/dist/shared/repo-cli-version.d.ts.map +1 -0
- package/dist/shared/repo-cli-version.js +63 -0
- package/dist/shared/runtime-source.d.ts +33 -0
- package/dist/shared/runtime-source.d.ts.map +1 -0
- package/dist/shared/runtime-source.js +156 -0
- package/dist/shared/version-compare.d.ts +7 -0
- package/dist/shared/version-compare.d.ts.map +1 -0
- package/dist/shared/version-compare.js +30 -0
- package/dist/shared/workflow-artifacts.d.ts +37 -0
- package/dist/shared/workflow-artifacts.d.ts.map +1 -0
- package/dist/shared/workflow-artifacts.js +97 -0
- package/package.json +2 -2
package/bin/agentplane.js
CHANGED
|
@@ -4,7 +4,9 @@ import path from "node:path";
|
|
|
4
4
|
import { stat } from "node:fs/promises";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { distExists, isPackageBuildFresh } from "./dist-guard.js";
|
|
7
|
-
import {
|
|
7
|
+
import { getWatchedRuntimePathsForPackage } from "./runtime-watch.js";
|
|
8
|
+
import { classifyStaleDistPolicy } from "./stale-dist-policy.js";
|
|
9
|
+
import { isPathInside, resolveFrameworkBinaryContext } from "./runtime-context.js";
|
|
8
10
|
|
|
9
11
|
async function exists(p) {
|
|
10
12
|
try {
|
|
@@ -43,6 +45,27 @@ function isRepoLocalHandoffInvocation() {
|
|
|
43
45
|
return (process.env.AGENTPLANE_REPO_LOCAL_HANDOFF ?? "").trim() === "1";
|
|
44
46
|
}
|
|
45
47
|
|
|
48
|
+
function inferRuntimeMode(context) {
|
|
49
|
+
if (context.inFrameworkCheckout && context.isRepoLocalRuntime) {
|
|
50
|
+
return isRepoLocalHandoffInvocation() ? "repo-local-handoff" : "repo-local";
|
|
51
|
+
}
|
|
52
|
+
if (context.inFrameworkCheckout && shouldUseGlobalBinaryInFramework()) {
|
|
53
|
+
return "global-forced-in-framework";
|
|
54
|
+
}
|
|
55
|
+
if (context.inFrameworkCheckout) {
|
|
56
|
+
return "global-in-framework";
|
|
57
|
+
}
|
|
58
|
+
return "global-installed";
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function primeRuntimeEnv(context) {
|
|
62
|
+
process.env.AGENTPLANE_RUNTIME_ACTIVE_BIN = context.thisBin;
|
|
63
|
+
process.env.AGENTPLANE_RUNTIME_MODE = inferRuntimeMode(context);
|
|
64
|
+
if (!isRepoLocalHandoffInvocation()) {
|
|
65
|
+
delete process.env.AGENTPLANE_RUNTIME_HANDOFF_FROM;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
46
69
|
function handoffToRepoLocalBinary(context) {
|
|
47
70
|
const repoBin = context.checkout?.repoBin;
|
|
48
71
|
if (!repoBin) return false;
|
|
@@ -57,6 +80,9 @@ function handoffToRepoLocalBinary(context) {
|
|
|
57
80
|
env: {
|
|
58
81
|
...process.env,
|
|
59
82
|
AGENTPLANE_REPO_LOCAL_HANDOFF: "1",
|
|
83
|
+
AGENTPLANE_RUNTIME_MODE: "repo-local-handoff",
|
|
84
|
+
AGENTPLANE_RUNTIME_ACTIVE_BIN: path.resolve(repoBin),
|
|
85
|
+
AGENTPLANE_RUNTIME_HANDOFF_FROM: context.thisBin,
|
|
60
86
|
},
|
|
61
87
|
});
|
|
62
88
|
|
|
@@ -91,11 +117,6 @@ function isHooksRunCommitMsgInvocation(argv) {
|
|
|
91
117
|
return false;
|
|
92
118
|
}
|
|
93
119
|
|
|
94
|
-
function isPathInside(baseDir, targetPath) {
|
|
95
|
-
const rel = path.relative(path.resolve(baseDir), path.resolve(targetPath));
|
|
96
|
-
return rel === "" || (!rel.startsWith("..") && !path.isAbsolute(rel));
|
|
97
|
-
}
|
|
98
|
-
|
|
99
120
|
async function assertDistUpToDate() {
|
|
100
121
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
101
122
|
const agentplaneRoot = path.resolve(here, "..");
|
|
@@ -120,11 +141,16 @@ async function assertDistUpToDate() {
|
|
|
120
141
|
{
|
|
121
142
|
name: "agentplane",
|
|
122
143
|
root: agentplaneRoot,
|
|
123
|
-
watchedPaths:
|
|
144
|
+
watchedPaths: getWatchedRuntimePathsForPackage("agentplane"),
|
|
124
145
|
},
|
|
125
146
|
];
|
|
126
|
-
if (await exists(path.join(coreRoot, "src")))
|
|
127
|
-
checks.push({
|
|
147
|
+
if (await exists(path.join(coreRoot, "src"))) {
|
|
148
|
+
checks.push({
|
|
149
|
+
name: "core",
|
|
150
|
+
root: coreRoot,
|
|
151
|
+
watchedPaths: getWatchedRuntimePathsForPackage("@agentplaneorg/core"),
|
|
152
|
+
});
|
|
153
|
+
}
|
|
128
154
|
|
|
129
155
|
const staleReasons = [];
|
|
130
156
|
for (const check of checks) {
|
|
@@ -150,6 +176,24 @@ async function assertDistUpToDate() {
|
|
|
150
176
|
return true;
|
|
151
177
|
}
|
|
152
178
|
|
|
179
|
+
const commandPolicy = classifyStaleDistPolicy(process.argv);
|
|
180
|
+
if (commandPolicy.mode === "warn_and_run") {
|
|
181
|
+
const commandText = process.argv
|
|
182
|
+
.slice(2)
|
|
183
|
+
.map((value) => String(value ?? "").trim())
|
|
184
|
+
.filter(Boolean)
|
|
185
|
+
.join(" ");
|
|
186
|
+
process.stderr.write(
|
|
187
|
+
"warning: allowing read-only diagnostic command to run with a stale repo build inside the framework checkout.\n" +
|
|
188
|
+
`command: ${commandText || "<unknown>"}\n` +
|
|
189
|
+
`detected: ${staleReasons.join(", ")}\n` +
|
|
190
|
+
"rebuild recommended:\n" +
|
|
191
|
+
" bun run --filter=@agentplaneorg/core build\n" +
|
|
192
|
+
" bun run --filter=agentplane build\n",
|
|
193
|
+
);
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
|
|
153
197
|
process.stderr.write(
|
|
154
198
|
"error: refusing to run a stale repo build (manifest/git quick-check failed).\n" +
|
|
155
199
|
"Fix:\n" +
|
|
@@ -165,6 +209,12 @@ async function assertDistUpToDate() {
|
|
|
165
209
|
return true;
|
|
166
210
|
}
|
|
167
211
|
|
|
212
|
+
const runtimeContext = resolveFrameworkBinaryContext({
|
|
213
|
+
cwd: process.cwd(),
|
|
214
|
+
thisBin: fileURLToPath(import.meta.url),
|
|
215
|
+
});
|
|
216
|
+
primeRuntimeEnv(runtimeContext);
|
|
217
|
+
|
|
168
218
|
if (!maybeHandoffToRepoLocalBinary()) {
|
|
169
219
|
await maybeWarnGlobalBinaryInRepoCheckout();
|
|
170
220
|
const ok = isHooksRunCommitMsgInvocation(process.argv) ? true : await assertDistUpToDate();
|
package/bin/dist-guard.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { execFileSync } from "node:child_process";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { readFile, stat } from "node:fs/promises";
|
|
4
|
+
import { collectWatchedRuntimeSnapshot, compareWatchedRuntimeSnapshots } from "./runtime-watch.js";
|
|
4
5
|
|
|
5
6
|
async function exists(p) {
|
|
6
7
|
try {
|
|
@@ -33,12 +34,13 @@ function resolveGitHead(cwd) {
|
|
|
33
34
|
}
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
function listGitPaths(cwd, args) {
|
|
37
|
+
function listGitPaths(cwd, args, options = {}) {
|
|
38
|
+
const trimLines = options.trimLines ?? true;
|
|
37
39
|
try {
|
|
38
40
|
const out = execFileSync("git", args, { cwd, encoding: "utf8" });
|
|
39
41
|
return out
|
|
40
42
|
.split(/\r?\n/u)
|
|
41
|
-
.map((line) => line.trim())
|
|
43
|
+
.map((line) => (trimLines ? line.trim() : line))
|
|
42
44
|
.filter(Boolean);
|
|
43
45
|
} catch {
|
|
44
46
|
return [];
|
|
@@ -50,14 +52,19 @@ function uniqueSorted(values) {
|
|
|
50
52
|
}
|
|
51
53
|
|
|
52
54
|
function workingTreeChangedPaths(cwd, watchedPaths) {
|
|
53
|
-
const lines = listGitPaths(
|
|
54
|
-
|
|
55
|
-
"--porcelain",
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
55
|
+
const lines = listGitPaths(
|
|
56
|
+
cwd,
|
|
57
|
+
["status", "--porcelain", "--untracked-files=all", "--", ...watchedPaths],
|
|
58
|
+
{ trimLines: false },
|
|
59
|
+
);
|
|
60
|
+
return uniqueSorted(
|
|
61
|
+
lines
|
|
62
|
+
.map((line) => {
|
|
63
|
+
const normalized = String(line ?? "");
|
|
64
|
+
return normalized.length > 3 ? normalized.slice(3).trim() : "";
|
|
65
|
+
})
|
|
66
|
+
.filter(Boolean),
|
|
67
|
+
);
|
|
61
68
|
}
|
|
62
69
|
|
|
63
70
|
function committedChangedPathsSince(cwd, fromGitHead, watchedPaths) {
|
|
@@ -77,6 +84,45 @@ async function fileMtimeMs(p) {
|
|
|
77
84
|
}
|
|
78
85
|
}
|
|
79
86
|
|
|
87
|
+
function parseManifestSnapshot(manifest) {
|
|
88
|
+
if (
|
|
89
|
+
!Array.isArray(manifest?.watched_runtime_paths) ||
|
|
90
|
+
!Array.isArray(manifest?.watched_runtime_files) ||
|
|
91
|
+
typeof manifest?.watched_runtime_snapshot_hash !== "string"
|
|
92
|
+
) {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const watchedPaths = manifest.watched_runtime_paths.filter((value) => typeof value === "string");
|
|
97
|
+
const files = manifest.watched_runtime_files
|
|
98
|
+
.filter(
|
|
99
|
+
(value) =>
|
|
100
|
+
value &&
|
|
101
|
+
typeof value === "object" &&
|
|
102
|
+
typeof value.path === "string" &&
|
|
103
|
+
typeof value.sha256 === "string" &&
|
|
104
|
+
typeof value.size_bytes === "number",
|
|
105
|
+
)
|
|
106
|
+
.map((value) => ({
|
|
107
|
+
path: value.path,
|
|
108
|
+
sha256: value.sha256,
|
|
109
|
+
size_bytes: value.size_bytes,
|
|
110
|
+
}));
|
|
111
|
+
|
|
112
|
+
if (
|
|
113
|
+
watchedPaths.length !== manifest.watched_runtime_paths.length ||
|
|
114
|
+
files.length !== manifest.watched_runtime_files.length
|
|
115
|
+
) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
watchedPaths,
|
|
121
|
+
files,
|
|
122
|
+
snapshotHash: manifest.watched_runtime_snapshot_hash,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
80
126
|
export async function isPackageBuildFresh(packageRoot, options = {}) {
|
|
81
127
|
const watchedPaths = options.watchedPaths ?? ["src"];
|
|
82
128
|
const manifestPath = path.join(packageRoot, "dist", ".build-manifest.json");
|
|
@@ -86,6 +132,28 @@ export async function isPackageBuildFresh(packageRoot, options = {}) {
|
|
|
86
132
|
}
|
|
87
133
|
|
|
88
134
|
const currentHead = resolveGitHead(packageRoot);
|
|
135
|
+
const manifestSnapshot = parseManifestSnapshot(manifest);
|
|
136
|
+
if (manifestSnapshot) {
|
|
137
|
+
const currentSnapshot = await collectWatchedRuntimeSnapshot(
|
|
138
|
+
packageRoot,
|
|
139
|
+
manifestSnapshot.watchedPaths,
|
|
140
|
+
);
|
|
141
|
+
const comparison = compareWatchedRuntimeSnapshots(manifestSnapshot, currentSnapshot);
|
|
142
|
+
if (!comparison.ok) {
|
|
143
|
+
return {
|
|
144
|
+
ok: false,
|
|
145
|
+
reason: "watched_runtime_snapshot_changed",
|
|
146
|
+
changedPaths: comparison.changedPaths,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (manifest.git_head && currentHead && manifest.git_head !== currentHead) {
|
|
151
|
+
return { ok: true, reason: "fresh_after_snapshot_match", changedPaths: [] };
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return { ok: true, reason: "fresh", changedPaths: [] };
|
|
155
|
+
}
|
|
156
|
+
|
|
89
157
|
const changedPaths = uniqueSorted([
|
|
90
158
|
...committedChangedPathsSince(packageRoot, manifest.git_head, watchedPaths),
|
|
91
159
|
...workingTreeChangedPaths(packageRoot, watchedPaths),
|
package/bin/runtime-context.d.ts
CHANGED
|
@@ -8,12 +8,15 @@ export type FrameworkCheckout = {
|
|
|
8
8
|
export type FrameworkBinaryContext = {
|
|
9
9
|
inFrameworkCheckout: boolean;
|
|
10
10
|
isRepoLocalBinary: boolean;
|
|
11
|
+
isRepoLocalRuntime: boolean;
|
|
11
12
|
checkout: FrameworkCheckout | null;
|
|
12
13
|
thisBin: string;
|
|
13
14
|
};
|
|
14
15
|
|
|
15
16
|
export function findFrameworkCheckout(startDir: string): FrameworkCheckout | null;
|
|
16
17
|
|
|
18
|
+
export function isPathInside(baseDir: string, targetPath: string): boolean;
|
|
19
|
+
|
|
17
20
|
export function resolveFrameworkBinaryContext(options: {
|
|
18
21
|
cwd: string;
|
|
19
22
|
thisBin: string;
|
package/bin/runtime-context.js
CHANGED
|
@@ -21,6 +21,7 @@ import path from "node:path";
|
|
|
21
21
|
* @typedef {{
|
|
22
22
|
* inFrameworkCheckout: boolean;
|
|
23
23
|
* isRepoLocalBinary: boolean;
|
|
24
|
+
* isRepoLocalRuntime: boolean;
|
|
24
25
|
* checkout: FrameworkCheckout | null;
|
|
25
26
|
* thisBin: string;
|
|
26
27
|
* }} FrameworkBinaryContext
|
|
@@ -55,6 +56,16 @@ export function findFrameworkCheckout(startDir) {
|
|
|
55
56
|
}
|
|
56
57
|
}
|
|
57
58
|
|
|
59
|
+
/**
|
|
60
|
+
* @param {string} baseDir
|
|
61
|
+
* @param {string} targetPath
|
|
62
|
+
* @returns {boolean}
|
|
63
|
+
*/
|
|
64
|
+
export function isPathInside(baseDir, targetPath) {
|
|
65
|
+
const rel = path.relative(path.resolve(baseDir), path.resolve(targetPath));
|
|
66
|
+
return rel === "" || (!rel.startsWith("..") && !path.isAbsolute(rel));
|
|
67
|
+
}
|
|
68
|
+
|
|
58
69
|
/**
|
|
59
70
|
* @param {FrameworkBinaryContextOptions} options
|
|
60
71
|
* @returns {FrameworkBinaryContext}
|
|
@@ -67,6 +78,7 @@ export function resolveFrameworkBinaryContext(options) {
|
|
|
67
78
|
return {
|
|
68
79
|
inFrameworkCheckout: false,
|
|
69
80
|
isRepoLocalBinary: false,
|
|
81
|
+
isRepoLocalRuntime: false,
|
|
70
82
|
checkout: null,
|
|
71
83
|
thisBin,
|
|
72
84
|
};
|
|
@@ -75,6 +87,7 @@ export function resolveFrameworkBinaryContext(options) {
|
|
|
75
87
|
return {
|
|
76
88
|
inFrameworkCheckout: true,
|
|
77
89
|
isRepoLocalBinary: path.resolve(checkout.repoBin) === thisBin,
|
|
90
|
+
isRepoLocalRuntime: isPathInside(checkout.packageRoot, thisBin),
|
|
78
91
|
checkout,
|
|
79
92
|
thisBin,
|
|
80
93
|
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export type WatchedRuntimeSnapshotFile = {
|
|
2
|
+
path: string;
|
|
3
|
+
sha256: string;
|
|
4
|
+
size_bytes: number;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export type WatchedRuntimeSnapshot = {
|
|
8
|
+
watchedPaths: string[];
|
|
9
|
+
files: WatchedRuntimeSnapshotFile[];
|
|
10
|
+
snapshotHash: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export type WatchedRuntimeSnapshotComparison = {
|
|
14
|
+
ok: boolean;
|
|
15
|
+
changedPaths: string[];
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export function getWatchedRuntimePathsForPackage(packageName: string): string[];
|
|
19
|
+
export function collectWatchedRuntimeSnapshot(
|
|
20
|
+
packageDir: string,
|
|
21
|
+
watchedPaths: string[],
|
|
22
|
+
): Promise<WatchedRuntimeSnapshot>;
|
|
23
|
+
export function compareWatchedRuntimeSnapshots(
|
|
24
|
+
recordedSnapshot: WatchedRuntimeSnapshot,
|
|
25
|
+
currentSnapshot: WatchedRuntimeSnapshot,
|
|
26
|
+
): WatchedRuntimeSnapshotComparison;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { readdir, readFile, stat } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
const WATCHED_RUNTIME_PATHS = {
|
|
6
|
+
agentplane: [
|
|
7
|
+
"src",
|
|
8
|
+
"bin/agentplane.js",
|
|
9
|
+
"bin/dist-guard.js",
|
|
10
|
+
"bin/runtime-context.js",
|
|
11
|
+
"bin/stale-dist-policy.js",
|
|
12
|
+
],
|
|
13
|
+
"@agentplaneorg/core": ["src"],
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
function normalizePath(value) {
|
|
17
|
+
return value.split(path.sep).join("/");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async function exists(targetPath) {
|
|
21
|
+
try {
|
|
22
|
+
await stat(targetPath);
|
|
23
|
+
return true;
|
|
24
|
+
} catch {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function walkFiles(targetPath) {
|
|
30
|
+
const targetStat = await stat(targetPath);
|
|
31
|
+
if (targetStat.isFile()) return [targetPath];
|
|
32
|
+
if (!targetStat.isDirectory()) return [];
|
|
33
|
+
|
|
34
|
+
const entries = await readdir(targetPath, { withFileTypes: true });
|
|
35
|
+
const files = [];
|
|
36
|
+
for (const entry of entries.toSorted((a, b) => a.name.localeCompare(b.name))) {
|
|
37
|
+
const childPath = path.join(targetPath, entry.name);
|
|
38
|
+
if (entry.isDirectory()) {
|
|
39
|
+
files.push(...(await walkFiles(childPath)));
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (entry.isFile()) files.push(childPath);
|
|
43
|
+
}
|
|
44
|
+
return files;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function fileHash(content) {
|
|
48
|
+
return createHash("sha256").update(content).digest("hex");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function snapshotHash(files) {
|
|
52
|
+
const digest = createHash("sha256");
|
|
53
|
+
for (const file of files) {
|
|
54
|
+
digest.update(file.path);
|
|
55
|
+
digest.update(":");
|
|
56
|
+
digest.update(file.sha256);
|
|
57
|
+
digest.update("\n");
|
|
58
|
+
}
|
|
59
|
+
return digest.digest("hex");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function getWatchedRuntimePathsForPackage(packageName) {
|
|
63
|
+
return [...(WATCHED_RUNTIME_PATHS[packageName] ?? ["src"])];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export async function collectWatchedRuntimeSnapshot(packageDir, watchedPaths) {
|
|
67
|
+
const normalizedWatchedPaths = [...new Set(watchedPaths.map((entry) => normalizePath(entry)))];
|
|
68
|
+
|
|
69
|
+
const absoluteFiles = [];
|
|
70
|
+
for (const watchedPath of normalizedWatchedPaths) {
|
|
71
|
+
const absolutePath = path.join(packageDir, watchedPath);
|
|
72
|
+
if (!(await exists(absolutePath))) continue;
|
|
73
|
+
absoluteFiles.push(...(await walkFiles(absolutePath)));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const uniqueAbsoluteFiles = [...new Set(absoluteFiles)].toSorted((a, b) => a.localeCompare(b));
|
|
77
|
+
const files = [];
|
|
78
|
+
for (const absolutePath of uniqueAbsoluteFiles) {
|
|
79
|
+
const content = await readFile(absolutePath);
|
|
80
|
+
files.push({
|
|
81
|
+
path: normalizePath(path.relative(packageDir, absolutePath)),
|
|
82
|
+
sha256: fileHash(content),
|
|
83
|
+
size_bytes: content.byteLength,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
watchedPaths: normalizedWatchedPaths,
|
|
89
|
+
files,
|
|
90
|
+
snapshotHash: snapshotHash(files),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function snapshotFileMap(snapshot) {
|
|
95
|
+
return new Map(snapshot.files.map((file) => [file.path, file.sha256]));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function compareWatchedRuntimeSnapshots(recordedSnapshot, currentSnapshot) {
|
|
99
|
+
if (
|
|
100
|
+
recordedSnapshot.snapshotHash === currentSnapshot.snapshotHash &&
|
|
101
|
+
recordedSnapshot.files.length === currentSnapshot.files.length
|
|
102
|
+
) {
|
|
103
|
+
return { ok: true, changedPaths: [] };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const recordedMap = snapshotFileMap(recordedSnapshot);
|
|
107
|
+
const currentMap = snapshotFileMap(currentSnapshot);
|
|
108
|
+
const changedPaths = [...new Set([...recordedMap.keys(), ...currentMap.keys()])]
|
|
109
|
+
.filter((filePath) => recordedMap.get(filePath) !== currentMap.get(filePath))
|
|
110
|
+
.toSorted((a, b) => a.localeCompare(b));
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
ok: changedPaths.length === 0,
|
|
114
|
+
changedPaths,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
function normalizeArgs(argv) {
|
|
2
|
+
return argv
|
|
3
|
+
.slice(2)
|
|
4
|
+
.map((value) => String(value ?? "").trim())
|
|
5
|
+
.filter(Boolean);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function isHelpOrVersionCommand(args) {
|
|
9
|
+
return (
|
|
10
|
+
args.length === 0 ||
|
|
11
|
+
args[0] === "--help" ||
|
|
12
|
+
args[0] === "-h" ||
|
|
13
|
+
args[0] === "--version" ||
|
|
14
|
+
args[0] === "-v" ||
|
|
15
|
+
args[0] === "help"
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function isConfigInspectionCommand(args) {
|
|
20
|
+
return args[0] === "config" && args[1] === "show";
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function isTaskInspectionCommand(args) {
|
|
24
|
+
return args[0] === "task" && ["list", "show", "verify-show"].includes(args[1] ?? "");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function isRoleOrQuickstartCommand(args) {
|
|
28
|
+
return args[0] === "quickstart" || args[0] === "role";
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function classifyStaleDistPolicy(argv = process.argv) {
|
|
32
|
+
const args = normalizeArgs(argv);
|
|
33
|
+
if (
|
|
34
|
+
args[0] === "doctor" ||
|
|
35
|
+
(args[0] === "runtime" && args[1] === "explain") ||
|
|
36
|
+
isHelpOrVersionCommand(args) ||
|
|
37
|
+
isRoleOrQuickstartCommand(args) ||
|
|
38
|
+
isConfigInspectionCommand(args) ||
|
|
39
|
+
isTaskInspectionCommand(args)
|
|
40
|
+
) {
|
|
41
|
+
return { mode: "warn_and_run", reason: "read_only_diagnostic" };
|
|
42
|
+
}
|
|
43
|
+
return { mode: "strict", reason: "default" };
|
|
44
|
+
}
|