agentloopkit 0.24.2 → 0.24.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/README.md +5 -5
- package/dist/cli/index.js +35 -9
- package/dist/cli/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -52,8 +52,8 @@ npx agentloopkit init
|
|
|
52
52
|
Pin the current version when you need repeatable CI or team setup:
|
|
53
53
|
|
|
54
54
|
```bash
|
|
55
|
-
npx --yes agentloopkit@0.24.
|
|
56
|
-
npx --yes agentloopkit@0.24.
|
|
55
|
+
npx --yes agentloopkit@0.24.4 version
|
|
56
|
+
npx --yes agentloopkit@0.24.4 init
|
|
57
57
|
```
|
|
58
58
|
|
|
59
59
|
Run the CLI after install:
|
|
@@ -269,7 +269,7 @@ Each contract records:
|
|
|
269
269
|
.agentloop/reports/YYYY-MM-DD-HH-mm-verification-report.md
|
|
270
270
|
```
|
|
271
271
|
|
|
272
|
-
Pass `--task .agentloop/tasks/file.md` to include task title, type, status, and path in the report.
|
|
272
|
+
Pass `--task .agentloop/tasks/file.md` to include task title, type, status, and path in the report. The path must point to a Markdown task contract inside the configured task directory. Invalid paths are reported as unavailable instead of being read.
|
|
273
273
|
|
|
274
274
|
It does not hide failures. Failed reports include a short failure summary with each failed command, exit code, and final useful output lines before the full command output. If long logs are truncated, the report keeps the first and last output so the final error stays visible. If no commands are configured, it writes a report saying nothing was verified.
|
|
275
275
|
|
|
@@ -384,7 +384,7 @@ See `docs/ci-summary.md`.
|
|
|
384
384
|
```bash
|
|
385
385
|
agentloop release-notes
|
|
386
386
|
agentloop release-notes --from v0.19.0 --to HEAD
|
|
387
|
-
agentloop release-notes --release-version 0.24.
|
|
387
|
+
agentloop release-notes --release-version 0.24.4
|
|
388
388
|
agentloop release-notes --json
|
|
389
389
|
agentloop release-notes --write
|
|
390
390
|
```
|
|
@@ -450,7 +450,7 @@ Use `agentloop check-gates --strict` as a review-evidence gate in pull request C
|
|
|
450
450
|
|
|
451
451
|
CI-generated verification reports include GitHub Actions provenance when available, so reviewers can trace an artifact back to the workflow run that created it.
|
|
452
452
|
|
|
453
|
-
See `docs/github-actions.md`, `examples/github-actions/`, `examples/gitlab-ci/`, and `examples/buildkite/` for copy-pasteable workflows. Pin `agentloopkit@0.24.
|
|
453
|
+
See `docs/github-actions.md`, `examples/github-actions/`, `examples/gitlab-ci/`, and `examples/buildkite/` for copy-pasteable workflows. Pin `agentloopkit@0.24.4` or a newer vetted release when reproducibility matters.
|
|
454
454
|
|
|
455
455
|
## PR Summaries
|
|
456
456
|
|
package/dist/cli/index.js
CHANGED
|
@@ -8,7 +8,7 @@ import { Command } from "commander";
|
|
|
8
8
|
|
|
9
9
|
// src/core/init.ts
|
|
10
10
|
import path6 from "path";
|
|
11
|
-
import { readdir as readdir3 } from "fs/promises";
|
|
11
|
+
import { readdir as readdir3, realpath } from "fs/promises";
|
|
12
12
|
import { homedir } from "os";
|
|
13
13
|
|
|
14
14
|
// src/core/constants.ts
|
|
@@ -395,6 +395,13 @@ ${section.trim()}
|
|
|
395
395
|
`);
|
|
396
396
|
result.updated.push(filePath);
|
|
397
397
|
}
|
|
398
|
+
async function resolveComparablePath(filePath) {
|
|
399
|
+
try {
|
|
400
|
+
return await realpath(filePath);
|
|
401
|
+
} catch {
|
|
402
|
+
return path6.resolve(filePath);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
398
405
|
async function initializeAgentLoop(options) {
|
|
399
406
|
const result = {
|
|
400
407
|
created: [],
|
|
@@ -404,7 +411,11 @@ async function initializeAgentLoop(options) {
|
|
|
404
411
|
};
|
|
405
412
|
const cwd = options.cwd;
|
|
406
413
|
const homeDirectory = options.homeDirectory ?? homedir();
|
|
407
|
-
|
|
414
|
+
const [resolvedCwd, resolvedHomeDirectory] = await Promise.all([
|
|
415
|
+
resolveComparablePath(cwd),
|
|
416
|
+
resolveComparablePath(homeDirectory)
|
|
417
|
+
]);
|
|
418
|
+
if (!options.force && resolvedCwd === resolvedHomeDirectory) {
|
|
408
419
|
throw new Error(
|
|
409
420
|
"Refusing to initialize your home directory. Run this inside a project repository, or pass --force if you intentionally want AgentLoopKit files in your home directory."
|
|
410
421
|
);
|
|
@@ -843,7 +854,17 @@ ${input.rollbackNotes || "Document how to revert or disable this change."}
|
|
|
843
854
|
async function createTaskContractFile(options) {
|
|
844
855
|
const createdDate = options.input.createdDate ?? formatDate();
|
|
845
856
|
const relativePath2 = options.out ?? path9.join(options.config.paths.tasksDir, `${createdDate}-${slugify(options.input.title)}.md`);
|
|
846
|
-
const absolutePath = path9.isAbsolute(relativePath2) ? relativePath2 : path9.
|
|
857
|
+
const absolutePath = path9.isAbsolute(relativePath2) ? path9.resolve(relativePath2) : path9.resolve(options.cwd, relativePath2);
|
|
858
|
+
const tasksRoot2 = path9.resolve(options.cwd, options.config.paths.tasksDir);
|
|
859
|
+
const relativeToTasks = path9.relative(tasksRoot2, absolutePath);
|
|
860
|
+
if (relativeToTasks === "" || relativeToTasks.startsWith("..") || path9.isAbsolute(relativeToTasks)) {
|
|
861
|
+
throw new AgentLoopError(
|
|
862
|
+
`Task output path must stay inside ${options.config.paths.tasksDir}.`
|
|
863
|
+
);
|
|
864
|
+
}
|
|
865
|
+
if (!absolutePath.endsWith(".md")) {
|
|
866
|
+
throw new AgentLoopError("Task output path must be a Markdown file.");
|
|
867
|
+
}
|
|
847
868
|
const markdown = generateTaskContract({ ...options.input, createdDate });
|
|
848
869
|
await writeTextFile(absolutePath, markdown);
|
|
849
870
|
return { path: absolutePath, markdown };
|
|
@@ -1098,10 +1119,16 @@ function isMarkdownTaskPath(taskPath) {
|
|
|
1098
1119
|
const segments = normalized.split("/").filter(Boolean);
|
|
1099
1120
|
return normalized.endsWith(".md") && !segments.some((segment) => segment === ".env" || segment.startsWith(".env."));
|
|
1100
1121
|
}
|
|
1101
|
-
|
|
1122
|
+
function isInside(parent, child) {
|
|
1123
|
+
const relative2 = path10.relative(parent, child);
|
|
1124
|
+
return relative2 === "" || !relative2.startsWith("..") && !path10.isAbsolute(relative2);
|
|
1125
|
+
}
|
|
1126
|
+
async function renderTaskContext(cwd, config, taskPath) {
|
|
1102
1127
|
if (!taskPath?.trim()) return "";
|
|
1103
1128
|
const cleanPath = taskPath.trim();
|
|
1104
|
-
|
|
1129
|
+
const absolutePath = path10.isAbsolute(cleanPath) ? path10.resolve(cleanPath) : path10.resolve(cwd, cleanPath);
|
|
1130
|
+
const tasksRoot2 = path10.resolve(cwd, config.paths.tasksDir);
|
|
1131
|
+
if (!isMarkdownTaskPath(cleanPath) || !isInside(tasksRoot2, absolutePath)) {
|
|
1105
1132
|
return `## Task Context
|
|
1106
1133
|
- Path: ${cleanPath}
|
|
1107
1134
|
- Status: unavailable
|
|
@@ -1109,7 +1136,6 @@ async function renderTaskContext(cwd, taskPath) {
|
|
|
1109
1136
|
|
|
1110
1137
|
`;
|
|
1111
1138
|
}
|
|
1112
|
-
const absolutePath = path10.isAbsolute(cleanPath) ? cleanPath : path10.join(cwd, cleanPath);
|
|
1113
1139
|
try {
|
|
1114
1140
|
const markdown = await readFile6(absolutePath, "utf8");
|
|
1115
1141
|
const metadata = parseTaskMetadata(markdown);
|
|
@@ -1168,7 +1194,7 @@ async function runVerification(options) {
|
|
|
1168
1194
|
const branch = await getGitBranch(options.cwd);
|
|
1169
1195
|
const commit = await getGitCommit(options.cwd);
|
|
1170
1196
|
const status = await getGitStatus(options.cwd);
|
|
1171
|
-
const taskContext = await renderTaskContext(options.cwd, options.taskPath);
|
|
1197
|
+
const taskContext = await renderTaskContext(options.cwd, options.config, options.taskPath);
|
|
1172
1198
|
const markdown = `# Verification Report
|
|
1173
1199
|
|
|
1174
1200
|
- Timestamp: ${nowIso}
|
|
@@ -1274,7 +1300,7 @@ function statePath(cwd, config) {
|
|
|
1274
1300
|
function toStoredPath(cwd, absolutePath) {
|
|
1275
1301
|
return path12.relative(cwd, absolutePath).split(path12.sep).join("/");
|
|
1276
1302
|
}
|
|
1277
|
-
function
|
|
1303
|
+
function isInside2(parent, child) {
|
|
1278
1304
|
const relative2 = path12.relative(parent, child);
|
|
1279
1305
|
return relative2 === "" || !relative2.startsWith("..") && !path12.isAbsolute(relative2);
|
|
1280
1306
|
}
|
|
@@ -1303,7 +1329,7 @@ async function resolveTaskPath(options) {
|
|
|
1303
1329
|
const absolutePath = path12.isAbsolute(options.taskPath) ? path12.resolve(options.taskPath) : path12.resolve(options.cwd, options.taskPath);
|
|
1304
1330
|
const root = tasksRoot(options.cwd, options.config);
|
|
1305
1331
|
const displayRoot = options.config.paths.tasksDir;
|
|
1306
|
-
if (!
|
|
1332
|
+
if (!isInside2(root, absolutePath)) {
|
|
1307
1333
|
if (!options.strict) return void 0;
|
|
1308
1334
|
throw new AgentLoopError(`Active task must be inside ${displayRoot}.`);
|
|
1309
1335
|
}
|