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.
Files changed (92) hide show
  1. package/dist/src/commands/add.js +84 -0
  2. package/dist/src/commands/agent.js +95 -0
  3. package/dist/src/commands/backup.js +46 -0
  4. package/dist/src/commands/doctor.js +80 -4
  5. package/dist/src/commands/hooks/claude.js +192 -0
  6. package/dist/src/commands/hooks/codex.js +191 -0
  7. package/dist/src/commands/hooks/index.js +23 -8
  8. package/dist/src/commands/hooks/install.js +11 -107
  9. package/dist/src/commands/hooks/resync.js +28 -18
  10. package/dist/src/commands/hooks/shared.js +94 -0
  11. package/dist/src/commands/hooks/status.js +13 -64
  12. package/dist/src/commands/hooks/uninstall.js +11 -47
  13. package/dist/src/commands/init.js +90 -28
  14. package/dist/src/commands/registry/index.js +1 -1
  15. package/dist/src/commands/sync.js +113 -0
  16. package/dist/src/commands/update.js +107 -0
  17. package/dist/src/core/agent-targets.js +50 -0
  18. package/dist/src/core/artifact-state.js +65 -0
  19. package/dist/src/core/atomic-file.js +72 -0
  20. package/dist/src/core/bundle-install.js +60 -38
  21. package/dist/src/core/bundles.js +21 -0
  22. package/dist/src/core/context/managed-block.js +190 -0
  23. package/dist/src/core/context/orchestrator.js +3 -1
  24. package/dist/src/core/context/project-constitution-inject.js +1 -1
  25. package/dist/src/core/context/regenerate.js +3 -3
  26. package/dist/src/core/context/strategies/codex-agents.js +100 -0
  27. package/dist/src/core/diagnostics/checks.js +16 -0
  28. package/dist/src/core/diagnostics/context.js +37 -8
  29. package/dist/src/core/diagnostics/provider-checks.js +219 -0
  30. package/dist/src/core/executor.js +30 -9
  31. package/dist/src/core/init/mutation-targets.js +139 -0
  32. package/dist/src/core/init/provider-facts.js +179 -0
  33. package/dist/src/core/init/steps.js +65 -9
  34. package/dist/src/core/install-planner.js +206 -0
  35. package/dist/src/core/install-transaction.js +405 -0
  36. package/dist/src/core/profile.js +1 -1
  37. package/dist/src/core/provider-artifacts.js +54 -0
  38. package/dist/src/core/provider-version.js +36 -0
  39. package/dist/src/core/reconciliation.js +61 -0
  40. package/dist/src/core/registries.js +10 -0
  41. package/dist/src/core/renderers/canonical-agent.js +27 -0
  42. package/dist/src/core/renderers/codex-agent.js +60 -0
  43. package/dist/src/core/skill-integrity.js +4 -3
  44. package/dist/src/index.js +97 -289
  45. package/dist/src/providers/index.js +143 -39
  46. package/dist/src/ui/provider-preflight.js +26 -0
  47. package/dist/src/utils/config.js +101 -9
  48. package/dist/tests/commands/agent.test.js +103 -0
  49. package/dist/tests/commands/backup.test.js +109 -0
  50. package/dist/tests/commands/doctor.test.js +65 -4
  51. package/dist/tests/commands/hooks/codex.test.js +230 -0
  52. package/dist/tests/commands/hooks/install.test.js +20 -1
  53. package/dist/tests/commands/hooks/resync.test.js +53 -6
  54. package/dist/tests/commands/hooks/status.test.js +10 -0
  55. package/dist/tests/commands/hooks/uninstall.test.js +12 -0
  56. package/dist/tests/commands/init.test.js +195 -8
  57. package/dist/tests/commands/multi-agent-targeting.test.js +231 -0
  58. package/dist/tests/core/agent-targets.test.js +45 -0
  59. package/dist/tests/core/artifact-state.test.js +144 -0
  60. package/dist/tests/core/atomic-file.test.js +96 -0
  61. package/dist/tests/core/bundle-install.test.js +208 -17
  62. package/dist/tests/core/bundles.test.js +12 -0
  63. package/dist/tests/core/context/managed-block.test.js +90 -0
  64. package/dist/tests/core/context/orchestrator.test.js +48 -3
  65. package/dist/tests/core/context/regenerate.test.js +1 -1
  66. package/dist/tests/core/context/strategies/codex-agents.test.js +185 -0
  67. package/dist/tests/core/context/strategies/config-instructions.test.js +1 -1
  68. package/dist/tests/core/diagnostics/checks.test.js +19 -0
  69. package/dist/tests/core/diagnostics/context.test.js +74 -0
  70. package/dist/tests/core/diagnostics/provider-checks.test.js +252 -0
  71. package/dist/tests/core/executor.test.js +20 -0
  72. package/dist/tests/core/export/engine.test.js +3 -2
  73. package/dist/tests/core/init/mutation-targets.test.js +235 -0
  74. package/dist/tests/core/init/orchestrator.test.js +1 -1
  75. package/dist/tests/core/init/provider-facts.test.js +177 -0
  76. package/dist/tests/core/init/steps.test.js +106 -2
  77. package/dist/tests/core/install-planner.test.js +224 -0
  78. package/dist/tests/core/install-transaction.test.js +257 -0
  79. package/dist/tests/core/provider-artifacts.test.js +64 -0
  80. package/dist/tests/core/provider-version.test.js +53 -0
  81. package/dist/tests/core/reconciliation.test.js +165 -0
  82. package/dist/tests/core/registries-sync.test.js +4 -4
  83. package/dist/tests/core/renderers/canonical-agent.test.js +58 -0
  84. package/dist/tests/core/renderers/codex-agent.test.js +128 -0
  85. package/dist/tests/core/versioning.test.js +1 -1
  86. package/dist/tests/integration/codex-provider-isolated.test.js +235 -0
  87. package/dist/tests/providers/hooks-config.test.js +43 -25
  88. package/dist/tests/providers/index.test.js +144 -39
  89. package/dist/tests/structural/codex-agent-escaping-completeness.test.js +77 -0
  90. package/dist/tests/ui/provider-preflight.test.js +25 -0
  91. package/dist/tests/utils/config.test.js +178 -18
  92. 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
- * Materializes a bundle and its dependency closure into the target agents.
32
- * The named bundle uses `scopeOverride` if given; dependencies always use
33
- * their own default scope (baseline→global, project→local, ambient→global).
34
- * Local installs resolve under `projectRoot`; global installs use the
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 installBundle(opts) {
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
- const installed = [];
42
- const skipped = [];
43
- for (const b of closure) {
44
- const contentDir = b.contentRoot ?? fallbackContentDir;
45
- const scope = b.name === opts.bundleName
46
- ? opts.scopeOverride ?? (0, bundles_1.defaultScopeForBundle)(b.scope)
47
- : (0, bundles_1.defaultScopeForBundle)(b.scope);
48
- for (const art of bundleArtifacts(b, contentDir)) {
49
- if (!fs_1.default.existsSync(art.sourcePath)) {
50
- skipped.push(`${art.name} (source missing: ${art.sourcePath})`);
51
- continue;
52
- }
53
- for (const agent of opts.agents) {
54
- if (providers_1.PROVIDERS[agent][art.type] === null) {
55
- skipped.push(`${art.name} (${agent}: ${art.type} unsupported)`);
56
- continue;
57
- }
58
- const rel = (0, providers_1.getTargetPath)(art.type, agent, scope);
59
- const baseDir = scope === 'local' ? path_1.default.join(opts.projectRoot, rel) : rel;
60
- const dest = path_1.default.join(baseDir, art.installName);
61
- (0, executor_1.installArtifact)(art.sourcePath, dest, opts.method);
62
- installed.push(`${art.name} ${agent} (${scope}) [${b.name}]`);
63
- }
64
- }
65
- }
66
- return { installed, skipped };
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 own artifacts (not just closure deps) were installed.
78
- const ownInstalled = summary.installed.filter((line) => line.endsWith(`[${opts.bundleName}]`));
79
- if (target && ownInstalled.length > 0) {
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
  }
@@ -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.PROVIDERS[agent];
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.PROVIDERS[agent].injection;
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 Object.keys(providers_1.PROVIDERS)) {
23
- const inj = providers_1.PROVIDERS[agent].injection;
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),