agentic-workflow-manager 3.1.0 → 3.2.1
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 +84 -0
- package/dist/src/commands/agent.js +95 -0
- package/dist/src/commands/backup.js +46 -0
- package/dist/src/commands/doctor.js +80 -4
- package/dist/src/commands/hooks/claude.js +192 -0
- package/dist/src/commands/hooks/codex.js +191 -0
- package/dist/src/commands/hooks/index.js +23 -8
- package/dist/src/commands/hooks/install.js +11 -107
- package/dist/src/commands/hooks/resync.js +28 -18
- package/dist/src/commands/hooks/shared.js +94 -0
- package/dist/src/commands/hooks/status.js +13 -64
- package/dist/src/commands/hooks/uninstall.js +11 -47
- package/dist/src/commands/init.js +90 -28
- package/dist/src/commands/registry/index.js +1 -1
- package/dist/src/commands/sync.js +113 -0
- package/dist/src/commands/update.js +107 -0
- package/dist/src/core/agent-targets.js +50 -0
- package/dist/src/core/artifact-state.js +65 -0
- package/dist/src/core/atomic-file.js +72 -0
- package/dist/src/core/bundle-install.js +60 -38
- package/dist/src/core/bundles.js +21 -0
- package/dist/src/core/context/managed-block.js +190 -0
- package/dist/src/core/context/orchestrator.js +3 -1
- package/dist/src/core/context/project-constitution-inject.js +1 -1
- package/dist/src/core/context/regenerate.js +3 -3
- package/dist/src/core/context/strategies/codex-agents.js +100 -0
- package/dist/src/core/diagnostics/checks.js +16 -0
- package/dist/src/core/diagnostics/context.js +37 -8
- package/dist/src/core/diagnostics/provider-checks.js +219 -0
- package/dist/src/core/executor.js +30 -9
- package/dist/src/core/init/mutation-targets.js +139 -0
- package/dist/src/core/init/provider-facts.js +179 -0
- package/dist/src/core/init/steps.js +65 -9
- package/dist/src/core/install-planner.js +206 -0
- package/dist/src/core/install-transaction.js +405 -0
- package/dist/src/core/profile.js +1 -1
- package/dist/src/core/provider-artifacts.js +54 -0
- package/dist/src/core/provider-version.js +36 -0
- package/dist/src/core/reconciliation.js +61 -0
- package/dist/src/core/registries.js +10 -0
- package/dist/src/core/renderers/canonical-agent.js +27 -0
- package/dist/src/core/renderers/codex-agent.js +60 -0
- package/dist/src/core/skill-integrity.js +4 -3
- package/dist/src/index.js +97 -289
- package/dist/src/providers/index.js +143 -39
- package/dist/src/ui/provider-preflight.js +26 -0
- package/dist/src/utils/config.js +101 -9
- package/dist/tests/commands/agent.test.js +103 -0
- package/dist/tests/commands/backup.test.js +109 -0
- package/dist/tests/commands/doctor.test.js +65 -4
- package/dist/tests/commands/hooks/codex.test.js +230 -0
- package/dist/tests/commands/hooks/install.test.js +20 -1
- package/dist/tests/commands/hooks/resync.test.js +53 -6
- package/dist/tests/commands/hooks/status.test.js +10 -0
- package/dist/tests/commands/hooks/uninstall.test.js +12 -0
- package/dist/tests/commands/init.test.js +195 -8
- package/dist/tests/commands/multi-agent-targeting.test.js +231 -0
- package/dist/tests/core/agent-targets.test.js +45 -0
- package/dist/tests/core/artifact-state.test.js +144 -0
- package/dist/tests/core/atomic-file.test.js +96 -0
- package/dist/tests/core/bundle-install.test.js +208 -17
- package/dist/tests/core/bundles.test.js +12 -0
- package/dist/tests/core/context/managed-block.test.js +90 -0
- package/dist/tests/core/context/orchestrator.test.js +48 -3
- package/dist/tests/core/context/regenerate.test.js +1 -1
- package/dist/tests/core/context/strategies/codex-agents.test.js +185 -0
- package/dist/tests/core/context/strategies/config-instructions.test.js +1 -1
- package/dist/tests/core/diagnostics/checks.test.js +19 -0
- package/dist/tests/core/diagnostics/context.test.js +74 -0
- package/dist/tests/core/diagnostics/provider-checks.test.js +252 -0
- package/dist/tests/core/executor.test.js +20 -0
- package/dist/tests/core/export/engine.test.js +3 -2
- package/dist/tests/core/init/mutation-targets.test.js +235 -0
- package/dist/tests/core/init/orchestrator.test.js +1 -1
- package/dist/tests/core/init/provider-facts.test.js +177 -0
- package/dist/tests/core/init/steps.test.js +106 -2
- package/dist/tests/core/install-planner.test.js +224 -0
- package/dist/tests/core/install-transaction.test.js +257 -0
- package/dist/tests/core/provider-artifacts.test.js +64 -0
- package/dist/tests/core/provider-version.test.js +53 -0
- package/dist/tests/core/reconciliation.test.js +165 -0
- package/dist/tests/core/registries-sync.test.js +4 -4
- package/dist/tests/core/renderers/canonical-agent.test.js +58 -0
- package/dist/tests/core/renderers/codex-agent.test.js +128 -0
- package/dist/tests/core/versioning.test.js +1 -1
- package/dist/tests/integration/codex-provider-isolated.test.js +235 -0
- package/dist/tests/providers/hooks-config.test.js +43 -25
- package/dist/tests/providers/index.test.js +144 -39
- package/dist/tests/structural/codex-agent-escaping-completeness.test.js +77 -0
- package/dist/tests/ui/provider-preflight.test.js +25 -0
- package/dist/tests/utils/config.test.js +178 -18
- package/package.json +1 -1
|
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.expandBundleArtifacts = expandBundleArtifacts;
|
|
6
7
|
exports.installBundle = installBundle;
|
|
7
8
|
exports.addBundle = addBundle;
|
|
8
9
|
exports.syncProfile = syncProfile;
|
|
@@ -10,10 +11,12 @@ exports.syncProfile = syncProfile;
|
|
|
10
11
|
const fs_1 = __importDefault(require("fs"));
|
|
11
12
|
const path_1 = __importDefault(require("path"));
|
|
12
13
|
const bundles_1 = require("./bundles");
|
|
13
|
-
const executor_1 = require("./executor");
|
|
14
14
|
const providers_1 = require("../providers");
|
|
15
15
|
const profile_1 = require("./profile");
|
|
16
16
|
const registries_1 = require("./registries");
|
|
17
|
+
const config_1 = require("../utils/config");
|
|
18
|
+
const install_planner_1 = require("./install-planner");
|
|
19
|
+
const install_transaction_1 = require("./install-transaction");
|
|
17
20
|
function bundleArtifacts(b, contentDir) {
|
|
18
21
|
const refs = [];
|
|
19
22
|
for (const s of b.skills) {
|
|
@@ -28,42 +31,46 @@ function bundleArtifacts(b, contentDir) {
|
|
|
28
31
|
return refs;
|
|
29
32
|
}
|
|
30
33
|
/**
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
* provider's absolute global path. Unsupported artifact types per agent and
|
|
36
|
-
* missing sources are skipped (never thrown).
|
|
34
|
+
* Expands a bundle and its dependency closure into the flat list of artifact
|
|
35
|
+
* intents to install. Pure — resolves the closure and content roots but
|
|
36
|
+
* performs no filesystem writes and no agent/scope resolution; that's
|
|
37
|
+
* `planInstall`'s job.
|
|
37
38
|
*/
|
|
38
|
-
function
|
|
39
|
+
function expandBundleArtifacts(opts) {
|
|
39
40
|
const fallbackContentDir = opts.contentDir ?? (0, registries_1.contentRoots)()[0] ?? '';
|
|
40
41
|
const closure = (0, bundles_1.resolveBundleClosure)(opts.bundleName, opts.bundles);
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
42
|
+
return closure.flatMap((b) => bundleArtifacts(b, b.contentRoot ?? fallbackContentDir));
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Materializes a bundle and its dependency closure into the target agents.
|
|
46
|
+
*
|
|
47
|
+
* `expandBundleArtifacts` turns the bundle closure into artifact intents,
|
|
48
|
+
* `planInstall` turns those into a deduped, ownership-tracked plan (see
|
|
49
|
+
* install-planner.ts for the shared-target and ownership rules), and
|
|
50
|
+
* `applyInstallPlan` (install-transaction.ts) performs the actual
|
|
51
|
+
* transactional filesystem apply — backups before any replace, rollback on
|
|
52
|
+
* verification failure, artifact-state persistence on success.
|
|
53
|
+
*
|
|
54
|
+
* Note: unlike the pre-Task-5 implementation, scope is now resolved once for
|
|
55
|
+
* the whole call (the named bundle's own scope, or `scopeOverride`) rather
|
|
56
|
+
* than per-bundle-in-closure; dependency bundles with a *different* default
|
|
57
|
+
* scope than the named bundle no longer get their own scope. This matches
|
|
58
|
+
* the plan for Task 5's `planInstall` signature (single `scope` param) and is
|
|
59
|
+
* expected to be revisited if that distinction turns out to matter.
|
|
60
|
+
*/
|
|
61
|
+
function installBundle(opts) {
|
|
62
|
+
const intents = expandBundleArtifacts(opts);
|
|
63
|
+
const enabledAgents = (0, config_1.getPreferences)().enabledAgents;
|
|
64
|
+
const plan = (0, install_planner_1.planInstall)({
|
|
65
|
+
artifacts: intents,
|
|
66
|
+
selectedAgents: opts.agents,
|
|
67
|
+
enabledAgents,
|
|
68
|
+
scope: opts.scopeOverride ?? (0, bundles_1.defaultScopeForBundle)(opts.bundles.find((bundle) => bundle.name === opts.bundleName)?.scope ?? 'baseline'),
|
|
69
|
+
projectRoot: opts.projectRoot,
|
|
70
|
+
method: opts.method,
|
|
71
|
+
});
|
|
72
|
+
const apply = opts.applyPlan ?? install_transaction_1.applyInstallPlan;
|
|
73
|
+
return apply(plan);
|
|
67
74
|
}
|
|
68
75
|
/**
|
|
69
76
|
* Installs a bundle (closure) and, when it is a project-scope bundle installed
|
|
@@ -74,9 +81,19 @@ function addBundle(opts) {
|
|
|
74
81
|
const summary = installBundle(opts);
|
|
75
82
|
const target = opts.bundles.find((b) => b.name === opts.bundleName);
|
|
76
83
|
let recordedExtension = null;
|
|
77
|
-
// Check the named bundle's
|
|
78
|
-
|
|
79
|
-
|
|
84
|
+
// Check the named bundle's OWN artifacts (not the dependency closure) were
|
|
85
|
+
// installed — i.e. at least one own artifact has a source on disk AND a
|
|
86
|
+
// type supported by at least one selected agent (matching what planInstall
|
|
87
|
+
// would actually materialize). ArtifactIntent/PlannedOperation carry no
|
|
88
|
+
// bundle provenance (install-planner.ts groups purely by physical target),
|
|
89
|
+
// so this is computed directly from the bundle's own artifact list rather
|
|
90
|
+
// than by inspecting `summary.installed`.
|
|
91
|
+
const ownInstalled = target
|
|
92
|
+
? bundleArtifacts(target, target.contentRoot ?? opts.contentDir ?? (0, registries_1.contentRoots)()[0] ?? '')
|
|
93
|
+
.some((artifact) => fs_1.default.existsSync(artifact.sourcePath) &&
|
|
94
|
+
opts.agents.some((agent) => (0, providers_1.providerFor)(agent)[artifact.type] !== null))
|
|
95
|
+
: false;
|
|
96
|
+
if (target && ownInstalled) {
|
|
80
97
|
const effective = opts.scopeOverride ?? (0, bundles_1.defaultScopeForBundle)(target.scope);
|
|
81
98
|
if ((0, profile_1.shouldRecordExtension)(target.scope, effective)) {
|
|
82
99
|
(0, profile_1.addExtension)(opts.projectRoot, opts.bundleName);
|
|
@@ -94,6 +111,8 @@ function syncProfile(opts) {
|
|
|
94
111
|
const profile = (0, profile_1.readProfile)(opts.projectRoot);
|
|
95
112
|
const installed = [];
|
|
96
113
|
const skipped = [];
|
|
114
|
+
const modifiedFiles = [];
|
|
115
|
+
const transactionIds = [];
|
|
97
116
|
for (const ext of profile.extensions) {
|
|
98
117
|
if (!opts.bundles.some((b) => b.name === ext)) {
|
|
99
118
|
skipped.push(`${ext} (bundle not found in registry — remove with \`awm remove ${ext}\`)`);
|
|
@@ -106,11 +125,14 @@ function syncProfile(opts) {
|
|
|
106
125
|
method: opts.method,
|
|
107
126
|
projectRoot: opts.projectRoot,
|
|
108
127
|
contentDir: opts.contentDir,
|
|
128
|
+
applyPlan: opts.applyPlan,
|
|
109
129
|
});
|
|
110
130
|
installed.push(...summary.installed);
|
|
111
131
|
skipped.push(...summary.skipped);
|
|
132
|
+
modifiedFiles.push(...summary.modifiedFiles);
|
|
133
|
+
transactionIds.push(summary.transactionId);
|
|
112
134
|
}
|
|
113
135
|
if (profile.extensions.length > 0)
|
|
114
136
|
(0, profile_1.ensureSkillsGitignored)(opts.projectRoot, opts.agents);
|
|
115
|
-
return { installed, skipped, extensions: profile.extensions };
|
|
137
|
+
return { installed, skipped, extensions: profile.extensions, transactionIds, modifiedFiles };
|
|
116
138
|
}
|
package/dist/src/core/bundles.js
CHANGED
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.readCatalog = readCatalog;
|
|
7
7
|
exports.discoverBundles = discoverBundles;
|
|
8
8
|
exports.resolveBundleSkills = resolveBundleSkills;
|
|
9
|
+
exports.resolveBundleAgents = resolveBundleAgents;
|
|
9
10
|
exports.defaultScopeForBundle = defaultScopeForBundle;
|
|
10
11
|
exports.discoverAllBundles = discoverAllBundles;
|
|
11
12
|
exports.resolveBundleClosure = resolveBundleClosure;
|
|
@@ -67,6 +68,26 @@ function resolveBundleSkills(bundleName, bundles) {
|
|
|
67
68
|
visit(bundleName);
|
|
68
69
|
return Array.from(skills);
|
|
69
70
|
}
|
|
71
|
+
/** Same closure-walk as `resolveBundleSkills`, over `agent`-type artifact names. */
|
|
72
|
+
function resolveBundleAgents(bundleName, bundles) {
|
|
73
|
+
const byName = new Map(bundles.map((b) => [b.name, b]));
|
|
74
|
+
const seen = new Set();
|
|
75
|
+
const agents = new Set();
|
|
76
|
+
const visit = (name) => {
|
|
77
|
+
if (seen.has(name))
|
|
78
|
+
return;
|
|
79
|
+
seen.add(name);
|
|
80
|
+
const b = byName.get(name);
|
|
81
|
+
if (!b)
|
|
82
|
+
return;
|
|
83
|
+
for (const dep of b.dependsOn)
|
|
84
|
+
visit(dep);
|
|
85
|
+
for (const a of b.agents)
|
|
86
|
+
agents.add(a);
|
|
87
|
+
};
|
|
88
|
+
visit(bundleName);
|
|
89
|
+
return Array.from(agents);
|
|
90
|
+
}
|
|
70
91
|
/**
|
|
71
92
|
* Default install scope for a bundle, derived from its scope class.
|
|
72
93
|
* baseline/ambient install globally; project bundles install locally.
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AWM_END = exports.AWM_START = void 0;
|
|
4
|
+
exports.normalizeManagedBody = normalizeManagedBody;
|
|
5
|
+
exports.managedBlockBody = managedBlockBody;
|
|
6
|
+
exports.mergeManagedBlock = mergeManagedBlock;
|
|
7
|
+
exports.removeManagedBlock = removeManagedBlock;
|
|
8
|
+
exports.AWM_START = '<!-- AWM:START -->';
|
|
9
|
+
exports.AWM_END = '<!-- AWM:END -->';
|
|
10
|
+
const BOUNDARY_PATTERN = /^<!-- AWM:BOUNDARY prefix=([0-2]) suffix=([0-1]) -->\n/u;
|
|
11
|
+
function assertText(value, name) {
|
|
12
|
+
if (typeof value !== 'string')
|
|
13
|
+
throw new Error(`${name} must be a string`);
|
|
14
|
+
}
|
|
15
|
+
function markerOffsets(input, marker) {
|
|
16
|
+
const offsets = [];
|
|
17
|
+
let offset = input.indexOf(marker);
|
|
18
|
+
while (offset !== -1) {
|
|
19
|
+
offsets.push(offset);
|
|
20
|
+
offset = input.indexOf(marker, offset + marker.length);
|
|
21
|
+
}
|
|
22
|
+
return offsets;
|
|
23
|
+
}
|
|
24
|
+
function insideMarkdownFence(input, offset) {
|
|
25
|
+
let fence = null;
|
|
26
|
+
for (const line of input.slice(0, offset).split(/\r?\n/u)) {
|
|
27
|
+
const match = line.match(/^[ ]{0,3}(`{3,}|~{3,})(.*)$/u);
|
|
28
|
+
if (!match)
|
|
29
|
+
continue;
|
|
30
|
+
const run = match[1];
|
|
31
|
+
if (!fence) {
|
|
32
|
+
fence = { character: run[0], length: run.length };
|
|
33
|
+
}
|
|
34
|
+
else if (run[0] === fence.character &&
|
|
35
|
+
run.length >= fence.length &&
|
|
36
|
+
match[2].trim().length === 0) {
|
|
37
|
+
fence = null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return fence !== null;
|
|
41
|
+
}
|
|
42
|
+
function validateMarkerOccurrences(input, prefix, marker) {
|
|
43
|
+
let offset = input.indexOf(prefix);
|
|
44
|
+
while (offset !== -1) {
|
|
45
|
+
if (!input.startsWith(marker, offset)) {
|
|
46
|
+
if (input.indexOf('-->', offset + prefix.length) === -1) {
|
|
47
|
+
throw new Error('incomplete AWM marker');
|
|
48
|
+
}
|
|
49
|
+
throw new Error('malformed AWM marker');
|
|
50
|
+
}
|
|
51
|
+
const after = offset + marker.length;
|
|
52
|
+
const startsLine = offset === 0 || input[offset - 1] === '\n';
|
|
53
|
+
const endsLine = after === input.length ||
|
|
54
|
+
input[after] === '\n' ||
|
|
55
|
+
(input[after] === '\r' && input[after + 1] === '\n');
|
|
56
|
+
if (!startsLine || !endsLine) {
|
|
57
|
+
throw new Error('AWM markers must be standalone delimiter lines');
|
|
58
|
+
}
|
|
59
|
+
if (insideMarkdownFence(input, offset)) {
|
|
60
|
+
throw new Error('AWM markers must be standalone delimiter lines outside code fences');
|
|
61
|
+
}
|
|
62
|
+
offset = input.indexOf(prefix, offset + prefix.length);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function inspect(input) {
|
|
66
|
+
validateMarkerOccurrences(input, '<!-- AWM:START', exports.AWM_START);
|
|
67
|
+
validateMarkerOccurrences(input, '<!-- AWM:END', exports.AWM_END);
|
|
68
|
+
const comments = input.match(/<!--[\s\S]*?-->/g) ?? [];
|
|
69
|
+
const malformed = comments.some((comment) => !comment.includes('AWM:BOUNDARY') &&
|
|
70
|
+
(comment.includes('AWM:START') || comment.includes('AWM:END')) &&
|
|
71
|
+
comment !== exports.AWM_START &&
|
|
72
|
+
comment !== exports.AWM_END);
|
|
73
|
+
if (malformed)
|
|
74
|
+
throw new Error('malformed AWM marker');
|
|
75
|
+
const starts = markerOffsets(input, exports.AWM_START);
|
|
76
|
+
const ends = markerOffsets(input, exports.AWM_END);
|
|
77
|
+
if (starts.length === 0 && ends.length === 0) {
|
|
78
|
+
if (input.includes('AWM:BOUNDARY')) {
|
|
79
|
+
throw new Error('malformed AWM boundary metadata');
|
|
80
|
+
}
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
if (starts.length !== ends.length)
|
|
84
|
+
throw new Error('unmatched AWM marker');
|
|
85
|
+
if (ends[0] < starts[0])
|
|
86
|
+
throw new Error('reversed AWM markers');
|
|
87
|
+
if (starts.length > 1) {
|
|
88
|
+
if (starts[1] < ends[0])
|
|
89
|
+
throw new Error('nested AWM markers');
|
|
90
|
+
throw new Error('duplicate AWM blocks');
|
|
91
|
+
}
|
|
92
|
+
return { start: starts[0], end: ends[0] };
|
|
93
|
+
}
|
|
94
|
+
function stripDelimiterNewlines(value) {
|
|
95
|
+
let result = value;
|
|
96
|
+
if (result.startsWith('\r\n'))
|
|
97
|
+
result = result.slice(2);
|
|
98
|
+
else if (result.startsWith('\n'))
|
|
99
|
+
result = result.slice(1);
|
|
100
|
+
if (result.endsWith('\r\n'))
|
|
101
|
+
result = result.slice(0, -2);
|
|
102
|
+
else if (result.endsWith('\n'))
|
|
103
|
+
result = result.slice(0, -1);
|
|
104
|
+
return result;
|
|
105
|
+
}
|
|
106
|
+
function contentOf(original, block) {
|
|
107
|
+
const rawContent = original.slice(block.start + exports.AWM_START.length, block.end);
|
|
108
|
+
const totalBoundaryOccurrences = original.split('AWM:BOUNDARY').length - 1;
|
|
109
|
+
const contentBoundaryOccurrences = rawContent.split('AWM:BOUNDARY').length - 1;
|
|
110
|
+
if (totalBoundaryOccurrences !== contentBoundaryOccurrences) {
|
|
111
|
+
throw new Error('malformed AWM boundary metadata');
|
|
112
|
+
}
|
|
113
|
+
let content = stripDelimiterNewlines(rawContent);
|
|
114
|
+
const boundaryOccurrences = content.split('AWM:BOUNDARY').length - 1;
|
|
115
|
+
if (boundaryOccurrences === 0)
|
|
116
|
+
return { body: content, boundary: null };
|
|
117
|
+
const boundaryMatch = content.match(BOUNDARY_PATTERN);
|
|
118
|
+
if (boundaryOccurrences !== 1 || !boundaryMatch) {
|
|
119
|
+
throw new Error('malformed AWM boundary metadata');
|
|
120
|
+
}
|
|
121
|
+
content = content.slice(boundaryMatch[0].length);
|
|
122
|
+
return {
|
|
123
|
+
body: content,
|
|
124
|
+
boundary: {
|
|
125
|
+
prefix: Number(boundaryMatch[1]),
|
|
126
|
+
suffix: Number(boundaryMatch[2]),
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
function render(body, boundary) {
|
|
131
|
+
const metadata = boundary
|
|
132
|
+
? `<!-- AWM:BOUNDARY prefix=${boundary.prefix} suffix=${boundary.suffix} -->\n`
|
|
133
|
+
: '';
|
|
134
|
+
return `${exports.AWM_START}\n${metadata}${body}\n${exports.AWM_END}`;
|
|
135
|
+
}
|
|
136
|
+
function normalizeManagedBody(body) {
|
|
137
|
+
assertText(body, 'body');
|
|
138
|
+
const normalized = body.replace(/(?:\r?\n)+$/u, '');
|
|
139
|
+
if (normalized.length === 0)
|
|
140
|
+
throw new Error('body must be non-empty');
|
|
141
|
+
if (normalized.includes('<!-- AWM:BOUNDARY')) {
|
|
142
|
+
throw new Error('body must not contain AWM boundary metadata');
|
|
143
|
+
}
|
|
144
|
+
if (inspect(normalized))
|
|
145
|
+
throw new Error('body must not contain AWM markers');
|
|
146
|
+
return normalized;
|
|
147
|
+
}
|
|
148
|
+
function managedBlockBody(original) {
|
|
149
|
+
assertText(original, 'original');
|
|
150
|
+
const block = inspect(original);
|
|
151
|
+
if (!block)
|
|
152
|
+
return null;
|
|
153
|
+
return contentOf(original, block).body;
|
|
154
|
+
}
|
|
155
|
+
function mergeManagedBlock(original, managedBody) {
|
|
156
|
+
assertText(original, 'original');
|
|
157
|
+
const body = normalizeManagedBody(managedBody);
|
|
158
|
+
const block = inspect(original);
|
|
159
|
+
if (block) {
|
|
160
|
+
const rendered = render(body, contentOf(original, block).boundary);
|
|
161
|
+
return original.slice(0, block.start) + rendered + original.slice(block.end + exports.AWM_END.length);
|
|
162
|
+
}
|
|
163
|
+
const separator = original.length === 0
|
|
164
|
+
? ''
|
|
165
|
+
: original.endsWith('\n\n')
|
|
166
|
+
? ''
|
|
167
|
+
: original.endsWith('\n') ? '\n' : '\n\n';
|
|
168
|
+
const boundary = { prefix: separator.length, suffix: 1 };
|
|
169
|
+
return `${original}${separator}${render(body, boundary)}\n`;
|
|
170
|
+
}
|
|
171
|
+
function removeManagedBlock(original) {
|
|
172
|
+
assertText(original, 'original');
|
|
173
|
+
const block = inspect(original);
|
|
174
|
+
if (!block)
|
|
175
|
+
return original;
|
|
176
|
+
const boundary = contentOf(original, block).boundary;
|
|
177
|
+
let ownedStart = block.start;
|
|
178
|
+
let ownedEnd = block.end + exports.AWM_END.length;
|
|
179
|
+
if (boundary) {
|
|
180
|
+
const prefix = '\n'.repeat(boundary.prefix);
|
|
181
|
+
const suffix = '\n'.repeat(boundary.suffix);
|
|
182
|
+
if (original.slice(block.start - prefix.length, block.start) === prefix) {
|
|
183
|
+
ownedStart -= prefix.length;
|
|
184
|
+
}
|
|
185
|
+
if (original.slice(ownedEnd, ownedEnd + suffix.length) === suffix) {
|
|
186
|
+
ownedEnd += suffix.length;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return original.slice(0, ownedStart) + original.slice(ownedEnd);
|
|
190
|
+
}
|
|
@@ -5,6 +5,7 @@ exports.InjectionOrchestrator = void 0;
|
|
|
5
5
|
const providers_1 = require("../../providers");
|
|
6
6
|
const hook_merge_1 = require("./strategies/hook-merge");
|
|
7
7
|
const config_instructions_1 = require("./strategies/config-instructions");
|
|
8
|
+
const codex_agents_1 = require("./strategies/codex-agents");
|
|
8
9
|
const provider_1 = require("./provider");
|
|
9
10
|
const materializer_1 = require("./materializer");
|
|
10
11
|
class InjectionOrchestrator {
|
|
@@ -13,7 +14,7 @@ class InjectionOrchestrator {
|
|
|
13
14
|
this.overrides = overrides;
|
|
14
15
|
}
|
|
15
16
|
provider(agent) {
|
|
16
|
-
return this.overrides.providerOverride ?? providers_1.
|
|
17
|
+
return this.overrides.providerOverride ?? (0, providers_1.providerFor)(agent);
|
|
17
18
|
}
|
|
18
19
|
strategy(agent) {
|
|
19
20
|
const inj = this.overrides.providerOverride !== undefined
|
|
@@ -24,6 +25,7 @@ class InjectionOrchestrator {
|
|
|
24
25
|
switch (inj.type) {
|
|
25
26
|
case 'cc-settings-merge': return new hook_merge_1.HookMergeStrategy();
|
|
26
27
|
case 'config-instructions': return new config_instructions_1.ConfigInstructionsStrategy();
|
|
28
|
+
case 'managed-agents-md': return new codex_agents_1.CodexAgentsStrategy();
|
|
27
29
|
}
|
|
28
30
|
}
|
|
29
31
|
/** Full input: builds context from registry and materializes to disk. Used by installContext only. */
|
|
@@ -16,7 +16,7 @@ const providers_1 = require("../../providers");
|
|
|
16
16
|
* cualquier agente futuro con inyección `config-instructions` hereda el trato.
|
|
17
17
|
*/
|
|
18
18
|
function injectProjectConstitution(projectRoot, agent) {
|
|
19
|
-
const inj = providers_1.
|
|
19
|
+
const inj = (0, providers_1.providerFor)(agent).injection;
|
|
20
20
|
if (!inj || inj.type !== 'config-instructions')
|
|
21
21
|
return 'not-applicable';
|
|
22
22
|
if (!fs_1.default.existsSync(path_1.default.join(projectRoot, 'CONSTITUTION.md')))
|
|
@@ -14,13 +14,13 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
14
14
|
const providers_1 = require("../../providers");
|
|
15
15
|
const registries_1 = require("../registries");
|
|
16
16
|
const orchestrator_1 = require("./orchestrator");
|
|
17
|
-
function regenerateGlobalContext(orch = new orchestrator_1.InjectionOrchestrator()) {
|
|
17
|
+
function regenerateGlobalContext(targets = [...providers_1.AGENT_TARGETS], orch = new orchestrator_1.InjectionOrchestrator()) {
|
|
18
18
|
const skillsRoot = (0, registries_1.capabilityRoot)('skills');
|
|
19
19
|
if (!skillsRoot)
|
|
20
20
|
return [];
|
|
21
21
|
const out = [];
|
|
22
|
-
for (const agent of
|
|
23
|
-
const inj = providers_1.
|
|
22
|
+
for (const agent of targets) {
|
|
23
|
+
const inj = (0, providers_1.providerFor)(agent).injection;
|
|
24
24
|
if (!inj || inj.type !== 'config-instructions')
|
|
25
25
|
continue;
|
|
26
26
|
if (!fs_1.default.existsSync(inj.configPath))
|
|
@@ -0,0 +1,100 @@
|
|
|
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
|
+
exports.CodexAgentsStrategy = void 0;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const paths_1 = require("../../paths");
|
|
10
|
+
const atomic_file_1 = require("../../atomic-file");
|
|
11
|
+
const managed_block_1 = require("../managed-block");
|
|
12
|
+
const provider_1 = require("../provider");
|
|
13
|
+
const PROJECT_GUIDANCE = [
|
|
14
|
+
'Read and obey `CONSTITUTION.md` before work when that file exists.',
|
|
15
|
+
'Use `.awm/profile.json` to declare project extensions, then run `awm sync`.',
|
|
16
|
+
'Run the verification commands declared by the project before completion.',
|
|
17
|
+
].join('\n');
|
|
18
|
+
function read(file) {
|
|
19
|
+
return fs_1.default.existsSync(file) ? fs_1.default.readFileSync(file, 'utf8') : '';
|
|
20
|
+
}
|
|
21
|
+
function injectFile(file, markdown) {
|
|
22
|
+
const original = read(file);
|
|
23
|
+
const merged = (0, managed_block_1.mergeManagedBlock)(original, markdown);
|
|
24
|
+
if (merged === original)
|
|
25
|
+
return 'unchanged';
|
|
26
|
+
(0, atomic_file_1.writeFileAtomic)(file, merged);
|
|
27
|
+
return 'injected';
|
|
28
|
+
}
|
|
29
|
+
class CodexAgentsStrategy {
|
|
30
|
+
globalPath(provider) {
|
|
31
|
+
const injection = provider.injection;
|
|
32
|
+
if (!injection || injection.type !== 'managed-agents-md') {
|
|
33
|
+
throw new Error('CodexAgentsStrategy requires a managed-agents-md provider');
|
|
34
|
+
}
|
|
35
|
+
if (typeof injection.globalPath !== 'string' || injection.globalPath.length === 0) {
|
|
36
|
+
throw new Error('managed-agents-md globalPath must be a non-empty string');
|
|
37
|
+
}
|
|
38
|
+
return injection.globalPath;
|
|
39
|
+
}
|
|
40
|
+
inject(input, provider) {
|
|
41
|
+
this.assertGlobalInput(input);
|
|
42
|
+
if (!fs_1.default.existsSync(input.ref.absPath)) {
|
|
43
|
+
throw new Error(`materialized context not found at ${input.ref.absPath}`);
|
|
44
|
+
}
|
|
45
|
+
const markdown = fs_1.default.readFileSync(input.ref.absPath, 'utf8');
|
|
46
|
+
return injectFile(this.globalPath(provider), markdown);
|
|
47
|
+
}
|
|
48
|
+
remove(input, provider) {
|
|
49
|
+
this.assertGlobalInput(input);
|
|
50
|
+
const file = this.globalPath(provider);
|
|
51
|
+
if (!fs_1.default.existsSync(file))
|
|
52
|
+
return;
|
|
53
|
+
const original = read(file);
|
|
54
|
+
const removed = (0, managed_block_1.removeManagedBlock)(original);
|
|
55
|
+
if (removed !== original)
|
|
56
|
+
(0, atomic_file_1.writeFileAtomic)(file, removed);
|
|
57
|
+
}
|
|
58
|
+
status(input, provider) {
|
|
59
|
+
this.assertGlobalInput(input);
|
|
60
|
+
const file = this.globalPath(provider);
|
|
61
|
+
if (!fs_1.default.existsSync(file))
|
|
62
|
+
return 'absent';
|
|
63
|
+
const body = (0, managed_block_1.managedBlockBody)(read(file));
|
|
64
|
+
if (body === null)
|
|
65
|
+
return 'absent';
|
|
66
|
+
if (!fs_1.default.existsSync(input.ref.absPath))
|
|
67
|
+
return 'stale';
|
|
68
|
+
const expected = fs_1.default.readFileSync(input.ref.absPath, 'utf8');
|
|
69
|
+
if ((0, provider_1.sha256)(expected) !== input.ref.contentHash)
|
|
70
|
+
return 'stale';
|
|
71
|
+
return body === (0, managed_block_1.normalizeManagedBody)(expected) ? 'injected' : 'stale';
|
|
72
|
+
}
|
|
73
|
+
injectGlobal(context) {
|
|
74
|
+
if (typeof context !== 'object' || context === null) {
|
|
75
|
+
throw new Error('context must be an object');
|
|
76
|
+
}
|
|
77
|
+
if (typeof context.markdown !== 'string' || context.markdown.length === 0) {
|
|
78
|
+
throw new Error('markdown must be a non-empty string');
|
|
79
|
+
}
|
|
80
|
+
return injectFile(path_1.default.join((0, paths_1.homeDir)(), '.codex', 'AGENTS.md'), context.markdown);
|
|
81
|
+
}
|
|
82
|
+
injectProject(projectRoot) {
|
|
83
|
+
if (typeof projectRoot !== 'string' || projectRoot.length === 0) {
|
|
84
|
+
throw new Error('projectRoot must be a non-empty string');
|
|
85
|
+
}
|
|
86
|
+
return injectFile(path_1.default.join(projectRoot, 'AGENTS.md'), PROJECT_GUIDANCE);
|
|
87
|
+
}
|
|
88
|
+
assertGlobalInput(input) {
|
|
89
|
+
if (typeof input !== 'object' || input === null) {
|
|
90
|
+
throw new Error('input must be an object');
|
|
91
|
+
}
|
|
92
|
+
if (input.scope !== 'global' || input.ref?.scope !== 'global') {
|
|
93
|
+
throw new Error('CodexAgentsStrategy supports only global injection');
|
|
94
|
+
}
|
|
95
|
+
if (typeof input.ref.absPath !== 'string' || input.ref.absPath.length === 0) {
|
|
96
|
+
throw new Error('input.ref.absPath must be a non-empty string');
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
exports.CodexAgentsStrategy = CodexAgentsStrategy;
|
|
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.computeProviderOverall = computeProviderOverall;
|
|
6
7
|
exports.runChecks = runChecks;
|
|
7
8
|
// src/core/diagnostics/checks.ts
|
|
8
9
|
const path_1 = __importDefault(require("path"));
|
|
@@ -126,6 +127,21 @@ function projectChecks(p) {
|
|
|
126
127
|
}
|
|
127
128
|
return out;
|
|
128
129
|
}
|
|
130
|
+
// --- Task 9: overall status for the per-provider diagnostic matrix --------
|
|
131
|
+
//
|
|
132
|
+
// Kept separate from `overall` above: that one degrades `CheckReport` (the
|
|
133
|
+
// machine+project report `init` renders before/after each run) on any
|
|
134
|
+
// 'missing' CheckResult. This one degrades the NEW provider matrix
|
|
135
|
+
// (doctor-only) on any check state that represents something genuinely
|
|
136
|
+
// broken/absent/unsupported — 'pending-trust'/'stale'/'pending' are
|
|
137
|
+
// actionable-but-expected transient states (rendered with ◷/⚠, not ✖) and
|
|
138
|
+
// do not by themselves degrade `overall`, mirroring how a 'warn' CheckResult
|
|
139
|
+
// doesn't degrade the machine+project report either.
|
|
140
|
+
const DEGRADING_PROVIDER_STATES = ['missing', 'unsupported', 'broken', 'absent', 'conflict'];
|
|
141
|
+
function computeProviderOverall(providers) {
|
|
142
|
+
const degraded = providers.some((p) => p.checks.some((c) => DEGRADING_PROVIDER_STATES.includes(c.state)));
|
|
143
|
+
return degraded ? 'degraded' : 'healthy';
|
|
144
|
+
}
|
|
129
145
|
function runChecks(ctx) {
|
|
130
146
|
const results = [
|
|
131
147
|
...machineChecks(ctx.machine),
|