agentic-workflow-manager 3.13.5 → 3.13.6
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/dist/src/commands/add.js +5 -0
- package/dist/src/commands/doctor.js +1 -0
- package/dist/src/commands/hooks/claude.js +15 -2
- package/dist/src/commands/hooks/shared.js +6 -1
- package/dist/src/commands/init.js +1 -0
- package/dist/src/commands/pin.js +24 -4
- package/dist/src/commands/preflight/checks.js +3 -2
- package/dist/src/commands/registry/add.js +1 -1
- package/dist/src/commands/sensors/index.js +7 -0
- package/dist/src/commands/sensors/init.js +48 -7
- package/dist/src/commands/sensors/install.js +20 -11
- package/dist/src/commands/sensors/run.js +31 -29
- package/dist/src/commands/sync.js +29 -0
- package/dist/src/commands/watch/index.js +11 -1
- package/dist/src/core/artifact-name.js +80 -0
- package/dist/src/core/bundle-install.js +55 -12
- package/dist/src/core/bundles.js +72 -13
- package/dist/src/core/context/regenerate.js +21 -4
- package/dist/src/core/context/strategies/codex-agents.js +18 -2
- package/dist/src/core/diagnostics/checks.js +26 -2
- package/dist/src/core/diagnostics/context.js +91 -25
- package/dist/src/core/diagnostics/provider-checks.js +62 -14
- package/dist/src/core/discovery.js +2 -2
- package/dist/src/core/init/mutation-targets.js +11 -0
- package/dist/src/core/init/steps.js +70 -19
- package/dist/src/core/install-planner.js +62 -17
- package/dist/src/core/install-transaction.js +13 -1
- package/dist/src/core/journal/adapter.js +10 -1
- package/dist/src/core/paths.js +61 -8
- package/dist/src/core/profile.js +12 -0
- package/dist/src/core/provider-artifacts.js +38 -13
- package/dist/src/core/provider-version.js +12 -6
- package/dist/src/core/registries.js +40 -19
- package/dist/src/core/renderers/registry.js +37 -0
- package/dist/src/core/skill-integrity.js +71 -10
- package/dist/src/core/update-check.js +14 -2
- package/dist/src/core/versioning.js +17 -2
- package/dist/src/index.js +31 -23
- package/dist/src/providers/index.js +7 -2
- package/dist/src/utils/config.js +7 -8
- package/dist/tests/commands/doctor.test.js +6 -0
- package/dist/tests/commands/pin.test.js +29 -2
- package/dist/tests/commands/preflight/preflight.test.js +31 -17
- package/dist/tests/commands/registry/install-bundles.test.js +6 -6
- package/dist/tests/commands/sensors/init-pack-unavailable.test.js +96 -0
- package/dist/tests/commands/sensors/install-settings-safety.test.js +87 -0
- package/dist/tests/commands/sensors/run-is-read-only.test.js +0 -0
- package/dist/tests/commands/sensors/run.test.js +24 -51
- package/dist/tests/commands/sensors/status-windows.test.js +39 -22
- package/dist/tests/commands/sensors/status.test.js +27 -12
- package/dist/tests/core/artifact-name-containment.test.js +86 -0
- package/dist/tests/core/context/agents-md-single-slot.test.js +69 -0
- package/dist/tests/core/context/regenerate.test.js +39 -8
- package/dist/tests/core/diagnostics/checks.test.js +14 -6
- package/dist/tests/core/diagnostics/provider-tier.test.js +3 -3
- package/dist/tests/core/diagnostics/rendered-artifact-visibility.test.js +69 -0
- package/dist/tests/core/init/all-baseline-bundles.test.js +64 -0
- package/dist/tests/core/init/context-injection-no-project.test.js +117 -0
- package/dist/tests/core/init/orchestrator.test.js +1 -1
- package/dist/tests/core/init/steps-registry-sync.test.js +1 -1
- package/dist/tests/core/init/steps.test.js +11 -5
- package/dist/tests/core/path-resolution-no-shell.test.js +136 -0
- package/dist/tests/core/paths.test.js +7 -34
- package/dist/tests/core/project-skill-links.test.js +159 -0
- package/dist/tests/core/provider-artifacts.test.js +66 -14
- package/dist/tests/core/provider-capability-guards.test.js +92 -0
- package/dist/tests/core/reconciliation.test.js +15 -15
- package/dist/tests/core/registry-manifest.test.js +1 -1
- package/dist/tests/core/semver-fails-closed.test.js +30 -0
- package/dist/tests/core/skill-integrity.test.js +10 -10
- package/dist/tests/core/skill-repair-safety.test.js +92 -0
- package/dist/tests/core/sync-profile-atomicity.test.js +122 -0
- package/dist/tests/integration/copilot-init-isolated.test.js +2 -2
- package/dist/tests/providers/index.test.js +1 -1
- package/dist/tests/structural/renderer-table-is-single-source.test.js +95 -0
- package/dist/tests/structural/symlink-type-is-explicit.test.js +73 -0
- package/dist/tests/utils/registry-view-overrides.test.js +2 -2
- package/package.json +4 -1
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
// Structural guard, in the vein of exec-invocation-explicit-stdio.test.ts: it does not
|
|
7
|
+
// test behaviour, it forbids the SHAPE that produced a whole family of bugs.
|
|
8
|
+
//
|
|
9
|
+
// "What filename does renderer R produce" existed in four places. Three agreed; the
|
|
10
|
+
// fourth — the one deciding whether a rendered artifact counted as installed — had
|
|
11
|
+
// never heard of `.mdc` or `.instructions.md`, so `awm add` re-installed the same
|
|
12
|
+
// Cursor/Copilot artifacts on every run and `awm doctor` called a healthy install
|
|
13
|
+
// absent. Each instance was individually reasonable; the class is what cost a release.
|
|
14
|
+
//
|
|
15
|
+
// `core/renderers/registry.ts` is now the single table. This test fails the moment a
|
|
16
|
+
// second one appears, whether by a new literal map or by a fresh `switch` over renderer
|
|
17
|
+
// ids somewhere that should have asked the table.
|
|
18
|
+
const fs_1 = __importDefault(require("fs"));
|
|
19
|
+
const path_1 = __importDefault(require("path"));
|
|
20
|
+
const SRC = path_1.default.join(__dirname, '..', '..', 'src');
|
|
21
|
+
const REGISTRY = path_1.default.join(SRC, 'core', 'renderers', 'registry.ts');
|
|
22
|
+
/** Every .ts file under src/, recursively. */
|
|
23
|
+
function sourceFiles(dir) {
|
|
24
|
+
const out = [];
|
|
25
|
+
for (const entry of fs_1.default.readdirSync(dir, { withFileTypes: true })) {
|
|
26
|
+
const full = path_1.default.join(dir, entry.name);
|
|
27
|
+
if (entry.isDirectory())
|
|
28
|
+
out.push(...sourceFiles(full));
|
|
29
|
+
else if (entry.name.endsWith('.ts'))
|
|
30
|
+
out.push(full);
|
|
31
|
+
}
|
|
32
|
+
return out;
|
|
33
|
+
}
|
|
34
|
+
/** The file's lines with `//` comments and blank lines dropped — a renderer id named in
|
|
35
|
+
* prose (a doc comment explaining the bug) is not a second implementation of the table. */
|
|
36
|
+
function codeLines(file) {
|
|
37
|
+
const raw = fs_1.default.readFileSync(file, 'utf-8').split('\n');
|
|
38
|
+
const out = [];
|
|
39
|
+
let inBlockComment = false;
|
|
40
|
+
raw.forEach((line, i) => {
|
|
41
|
+
let l = line;
|
|
42
|
+
if (inBlockComment) {
|
|
43
|
+
const end = l.indexOf('*/');
|
|
44
|
+
if (end === -1)
|
|
45
|
+
return;
|
|
46
|
+
inBlockComment = false;
|
|
47
|
+
l = l.slice(end + 2);
|
|
48
|
+
}
|
|
49
|
+
const start = l.indexOf('/*');
|
|
50
|
+
if (start !== -1) {
|
|
51
|
+
inBlockComment = l.indexOf('*/', start) === -1;
|
|
52
|
+
l = l.slice(0, start) + (inBlockComment ? '' : l.slice(l.indexOf('*/', start) + 2));
|
|
53
|
+
}
|
|
54
|
+
const lineComment = l.indexOf('//');
|
|
55
|
+
if (lineComment !== -1)
|
|
56
|
+
l = l.slice(0, lineComment);
|
|
57
|
+
if (l.trim())
|
|
58
|
+
out.push({ line: l, n: i + 1 });
|
|
59
|
+
});
|
|
60
|
+
return out;
|
|
61
|
+
}
|
|
62
|
+
/** The extensions the renderer table owns. A literal here, outside the table, IS the bug. */
|
|
63
|
+
const RENDERED_EXTENSIONS = ['.toml', '.mdc', '.instructions.md'];
|
|
64
|
+
describe('the renderer table is the only renderer table', () => {
|
|
65
|
+
const files = sourceFiles(SRC).filter((f) => f !== REGISTRY);
|
|
66
|
+
it('no other source file maps a renderer id to a file extension', () => {
|
|
67
|
+
const offenders = [];
|
|
68
|
+
for (const file of files) {
|
|
69
|
+
for (const { line, n } of codeLines(file)) {
|
|
70
|
+
// A renderer-to-extension mapping looks like an id and an extension on
|
|
71
|
+
// the same line of code. That is exactly the shape of every copy that
|
|
72
|
+
// drifted, and nothing legitimate needs to write both at once now that
|
|
73
|
+
// `rendererExtension`/`renderedFilename` exist.
|
|
74
|
+
const namesRenderer = /'(codex-agent-toml|cursor-mdc|copilot-instructions)'/.test(line);
|
|
75
|
+
const namesExtension = RENDERED_EXTENSIONS.some((e) => line.includes(`'${e}'`) || line.includes(`"${e}"`));
|
|
76
|
+
if (namesRenderer && namesExtension) {
|
|
77
|
+
offenders.push(`${path_1.default.relative(SRC, file)}:${n}: ${line.trim()}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
expect(offenders).toEqual([]);
|
|
82
|
+
});
|
|
83
|
+
it('the table itself still covers every declared renderer', () => {
|
|
84
|
+
// The guard above is only worth anything if the one surviving table is complete:
|
|
85
|
+
// a renderer added to `RendererId` but not to the table would make the guard
|
|
86
|
+
// push every caller toward a lookup that has no answer.
|
|
87
|
+
const { RENDERER_IDS } = require('../../src/providers');
|
|
88
|
+
const { rendererExtension } = require('../../src/core/renderers/registry');
|
|
89
|
+
for (const id of RENDERER_IDS) {
|
|
90
|
+
// `null` (the `link` renderer, which keeps the name as-is) is an ANSWER;
|
|
91
|
+
// `undefined` is the table not knowing about the renderer at all.
|
|
92
|
+
expect(rendererExtension(id)).not.toBeUndefined();
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
// Structural guard, same family as exec-invocation-explicit-stdio.test.ts.
|
|
7
|
+
//
|
|
8
|
+
// `fs.symlinkSync(target, dest)` with no third argument creates a DIRECTORY symlink on
|
|
9
|
+
// Windows when the target is a directory, and that needs SeCreateSymbolicLinkPrivilege
|
|
10
|
+
// — denied by default on unprivileged accounts, including GitHub Actions'
|
|
11
|
+
// windows-latest runner. The same omission has been fixed one site at a time, more
|
|
12
|
+
// than once, each fix landing where the bug was reported and nowhere else:
|
|
13
|
+
// `executor.ts` learned `'junction'`, and `skill-integrity.ts` did not, for a whole
|
|
14
|
+
// release — its comment still says "este sitio nunca recibio el fix".
|
|
15
|
+
//
|
|
16
|
+
// So the rule is about the CALL, not about any one call site: every `fs.symlinkSync`
|
|
17
|
+
// either passes an explicit type argument, or is wrapped in a `try` whose `catch`
|
|
18
|
+
// falls back to a copy. Those are the only two ways this survives Windows, and one of
|
|
19
|
+
// them has to be visible at the call.
|
|
20
|
+
const fs_1 = __importDefault(require("fs"));
|
|
21
|
+
const path_1 = __importDefault(require("path"));
|
|
22
|
+
const SRC = path_1.default.join(__dirname, '..', '..', 'src');
|
|
23
|
+
function sourceFiles(dir) {
|
|
24
|
+
const out = [];
|
|
25
|
+
for (const entry of fs_1.default.readdirSync(dir, { withFileTypes: true })) {
|
|
26
|
+
const full = path_1.default.join(dir, entry.name);
|
|
27
|
+
if (entry.isDirectory())
|
|
28
|
+
out.push(...sourceFiles(full));
|
|
29
|
+
else if (entry.name.endsWith('.ts'))
|
|
30
|
+
out.push(full);
|
|
31
|
+
}
|
|
32
|
+
return out;
|
|
33
|
+
}
|
|
34
|
+
/** True when `lines[i]` sits inside a try block whose catch copies instead. Deliberately
|
|
35
|
+
* shallow — it looks a few lines ahead for the catch, which is what every real fallback
|
|
36
|
+
* in this codebase looks like. A cleverer arrangement that defeats it should be written
|
|
37
|
+
* with an explicit type argument instead. */
|
|
38
|
+
function hasCopyFallback(lines, i) {
|
|
39
|
+
const window = lines.slice(Math.max(0, i - 3), i + 8).join('\n');
|
|
40
|
+
return /\btry\b/.test(window) && /catch\b/.test(window) && /copyFileSync|cpSync/.test(window);
|
|
41
|
+
}
|
|
42
|
+
describe('every symlink creation survives Windows on purpose', () => {
|
|
43
|
+
it('passes an explicit type argument or falls back to a copy', () => {
|
|
44
|
+
const offenders = [];
|
|
45
|
+
for (const file of sourceFiles(SRC)) {
|
|
46
|
+
const lines = fs_1.default.readFileSync(file, 'utf-8').split('\n');
|
|
47
|
+
lines.forEach((line, i) => {
|
|
48
|
+
if (!/\bfs\.symlinkSync\s*\(/.test(line))
|
|
49
|
+
return;
|
|
50
|
+
// Three arguments — the third is the type ('junction' | 'dir' | 'file').
|
|
51
|
+
const explicitType = /symlinkSync\s*\([^)]*,[^)]*,\s*[^)]+\)/.test(line) ||
|
|
52
|
+
/'junction'|'dir'|'file'/.test(lines.slice(i, i + 3).join('\n'));
|
|
53
|
+
if (explicitType || hasCopyFallback(lines, i))
|
|
54
|
+
return;
|
|
55
|
+
offenders.push(`${path_1.default.relative(SRC, file)}:${i + 1}: ${line.trim()}`);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
expect(offenders).toEqual([]);
|
|
59
|
+
});
|
|
60
|
+
it('the guard actually rejects a bare two-argument call', () => {
|
|
61
|
+
// Without this, a guard whose regex silently stopped matching would read as a
|
|
62
|
+
// clean bill of health forever — the failure mode of every structural test.
|
|
63
|
+
const bare = ['fs.symlinkSync(source, dest);'];
|
|
64
|
+
const explicit = ["fs.symlinkSync(source, dest, 'junction');"];
|
|
65
|
+
const wrapped = ['try {', 'fs.symlinkSync(source, dest);', '} catch {', 'fs.copyFileSync(source, dest);', '}'];
|
|
66
|
+
const rejects = (lines, i) => !/symlinkSync\s*\([^)]*,[^)]*,\s*[^)]+\)/.test(lines[i]) &&
|
|
67
|
+
!/'junction'|'dir'|'file'/.test(lines.slice(i, i + 3).join('\n')) &&
|
|
68
|
+
!hasCopyFallback(lines, i);
|
|
69
|
+
expect(rejects(bare, 0)).toBe(true);
|
|
70
|
+
expect(rejects(explicit, 0)).toBe(false);
|
|
71
|
+
expect(rejects(wrapped, 1)).toBe(false);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
@@ -25,9 +25,9 @@ describe('registry-view override markers', () => {
|
|
|
25
25
|
fs_1.default.rmSync(tmpHome, { recursive: true, force: true });
|
|
26
26
|
});
|
|
27
27
|
it('packageDetailLines marks an overridden skill with its registry name', () => {
|
|
28
|
-
const {
|
|
28
|
+
const { registriesDir } = require('../../src/core/registries');
|
|
29
29
|
const { buildPackageView, packageDetailLines } = require('../../src/utils/registry-view');
|
|
30
|
-
const teamSkillPath = path_1.default.join(
|
|
30
|
+
const teamSkillPath = path_1.default.join(registriesDir(), 'team-acme', 'skills', 'brainstorming');
|
|
31
31
|
const view = buildPackageView([{ name: 'brainstorming', path: teamSkillPath, description: 'd', overrode: '/old/path' }], [], [], []);
|
|
32
32
|
const lines = packageDetailLines(view[0]).join('\n');
|
|
33
33
|
expect(lines).toContain('brainstorming');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentic-workflow-manager",
|
|
3
|
-
"version": "3.13.
|
|
3
|
+
"version": "3.13.6",
|
|
4
4
|
"main": "dist/src/index.js",
|
|
5
5
|
"bin": {
|
|
6
6
|
"awm": "./dist/src/index.js"
|
|
@@ -34,6 +34,9 @@
|
|
|
34
34
|
],
|
|
35
35
|
"author": "Kodria",
|
|
36
36
|
"license": "ISC",
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=22"
|
|
39
|
+
},
|
|
37
40
|
"description": "CLI to manage Agentic Workflows - install skills, workflows, and processes for AI agents",
|
|
38
41
|
"dependencies": {
|
|
39
42
|
"@clack/prompts": "^1.0.1",
|