agentic-workflow-manager 2.0.0

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 (145) hide show
  1. package/README.md +62 -0
  2. package/dist/src/commands/doctor.js +77 -0
  3. package/dist/src/commands/hooks/index.js +118 -0
  4. package/dist/src/commands/hooks/install.js +113 -0
  5. package/dist/src/commands/hooks/resync.js +50 -0
  6. package/dist/src/commands/hooks/status.js +74 -0
  7. package/dist/src/commands/hooks/uninstall.js +59 -0
  8. package/dist/src/commands/init.js +181 -0
  9. package/dist/src/commands/ledger/index.js +73 -0
  10. package/dist/src/commands/pin.js +75 -0
  11. package/dist/src/commands/registry/add.js +62 -0
  12. package/dist/src/commands/registry/index.js +166 -0
  13. package/dist/src/commands/registry/install-bundles.js +36 -0
  14. package/dist/src/commands/registry/remove.js +18 -0
  15. package/dist/src/commands/registry/status.js +31 -0
  16. package/dist/src/commands/sensors/baseline.js +93 -0
  17. package/dist/src/commands/sensors/formatters/eslint.js +29 -0
  18. package/dist/src/commands/sensors/formatters/generic.js +8 -0
  19. package/dist/src/commands/sensors/formatters/semgrep.js +18 -0
  20. package/dist/src/commands/sensors/formatters/test.js +12 -0
  21. package/dist/src/commands/sensors/formatters/tsc.js +23 -0
  22. package/dist/src/commands/sensors/index.js +94 -0
  23. package/dist/src/commands/sensors/init.js +112 -0
  24. package/dist/src/commands/sensors/install.js +78 -0
  25. package/dist/src/commands/sensors/run.js +217 -0
  26. package/dist/src/commands/sensors/status.js +85 -0
  27. package/dist/src/commands/sensors/types.js +2 -0
  28. package/dist/src/core/bundle-install.js +116 -0
  29. package/dist/src/core/bundles.js +122 -0
  30. package/dist/src/core/cli-version.js +30 -0
  31. package/dist/src/core/context/materializer.js +30 -0
  32. package/dist/src/core/context/orchestrator.js +75 -0
  33. package/dist/src/core/context/project-constitution-inject.js +47 -0
  34. package/dist/src/core/context/provider.js +29 -0
  35. package/dist/src/core/context/regenerate.js +61 -0
  36. package/dist/src/core/context/strategies/config-instructions.js +73 -0
  37. package/dist/src/core/context/strategies/hook-merge.js +23 -0
  38. package/dist/src/core/context/strategies/strategy.js +2 -0
  39. package/dist/src/core/context/types.js +2 -0
  40. package/dist/src/core/diagnostics/checks.js +141 -0
  41. package/dist/src/core/diagnostics/context.js +181 -0
  42. package/dist/src/core/diagnostics/types.js +2 -0
  43. package/dist/src/core/discovery.js +135 -0
  44. package/dist/src/core/executor.js +41 -0
  45. package/dist/src/core/init/detector.js +67 -0
  46. package/dist/src/core/init/orchestrator.js +54 -0
  47. package/dist/src/core/init/steps.js +279 -0
  48. package/dist/src/core/init/types.js +2 -0
  49. package/dist/src/core/ledger/store.js +73 -0
  50. package/dist/src/core/ledger/types.js +2 -0
  51. package/dist/src/core/miro.js +314 -0
  52. package/dist/src/core/profile-pins.js +36 -0
  53. package/dist/src/core/profile.js +146 -0
  54. package/dist/src/core/registries.js +205 -0
  55. package/dist/src/core/registry.js +28 -0
  56. package/dist/src/core/skill-integrity.js +97 -0
  57. package/dist/src/core/story-map-parser.js +83 -0
  58. package/dist/src/core/update-check-worker.js +10 -0
  59. package/dist/src/core/update-check.js +106 -0
  60. package/dist/src/core/versioning.js +112 -0
  61. package/dist/src/index.js +689 -0
  62. package/dist/src/providers/index.js +63 -0
  63. package/dist/src/utils/config.js +35 -0
  64. package/dist/src/utils/grouping.js +51 -0
  65. package/dist/src/utils/registry-view.js +154 -0
  66. package/dist/tests/commands/doctor.test.js +98 -0
  67. package/dist/tests/commands/hooks/install.test.js +149 -0
  68. package/dist/tests/commands/hooks/resync.test.js +135 -0
  69. package/dist/tests/commands/hooks/router.test.js +26 -0
  70. package/dist/tests/commands/hooks/status.test.js +88 -0
  71. package/dist/tests/commands/hooks/uninstall.test.js +104 -0
  72. package/dist/tests/commands/init.test.js +87 -0
  73. package/dist/tests/commands/ledger/index.test.js +64 -0
  74. package/dist/tests/commands/pin.test.js +72 -0
  75. package/dist/tests/commands/registry/add.test.js +223 -0
  76. package/dist/tests/commands/registry/install-bundles.test.js +87 -0
  77. package/dist/tests/commands/registry/remove.test.js +49 -0
  78. package/dist/tests/commands/registry/status.test.js +43 -0
  79. package/dist/tests/commands/sensors/baseline.test.js +106 -0
  80. package/dist/tests/commands/sensors/formatters/eslint.test.js +32 -0
  81. package/dist/tests/commands/sensors/formatters/semgrep.test.js +38 -0
  82. package/dist/tests/commands/sensors/formatters/tsc.test.js +25 -0
  83. package/dist/tests/commands/sensors/index.test.js +18 -0
  84. package/dist/tests/commands/sensors/init.test.js +137 -0
  85. package/dist/tests/commands/sensors/install.test.js +60 -0
  86. package/dist/tests/commands/sensors/router.test.js +23 -0
  87. package/dist/tests/commands/sensors/run.test.js +353 -0
  88. package/dist/tests/commands/sensors/status.test.js +98 -0
  89. package/dist/tests/core/base-remote.test.js +70 -0
  90. package/dist/tests/core/bundle-install.test.js +198 -0
  91. package/dist/tests/core/bundles-multiroot.test.js +68 -0
  92. package/dist/tests/core/bundles-overrides.test.js +49 -0
  93. package/dist/tests/core/bundles.test.js +96 -0
  94. package/dist/tests/core/cli-version.test.js +17 -0
  95. package/dist/tests/core/context/materializer.test.js +46 -0
  96. package/dist/tests/core/context/orchestrator.test.js +127 -0
  97. package/dist/tests/core/context/project-constitution-inject.test.js +82 -0
  98. package/dist/tests/core/context/provider.test.js +45 -0
  99. package/dist/tests/core/context/regenerate.test.js +106 -0
  100. package/dist/tests/core/context/strategies/config-instructions.test.js +88 -0
  101. package/dist/tests/core/context/strategies/hook-merge.test.js +71 -0
  102. package/dist/tests/core/context/types.test.js +15 -0
  103. package/dist/tests/core/diagnostics/checks.test.js +208 -0
  104. package/dist/tests/core/diagnostics/context.test.js +170 -0
  105. package/dist/tests/core/discovery-multiroot.test.js +63 -0
  106. package/dist/tests/core/discovery-overrides.test.js +105 -0
  107. package/dist/tests/core/discovery.test.js +113 -0
  108. package/dist/tests/core/executor.test.js +44 -0
  109. package/dist/tests/core/init/detector.test.js +56 -0
  110. package/dist/tests/core/init/orchestrator.test.js +122 -0
  111. package/dist/tests/core/init/steps.test.js +363 -0
  112. package/dist/tests/core/ledger/gitignore.test.js +13 -0
  113. package/dist/tests/core/ledger/store.test.js +122 -0
  114. package/dist/tests/core/miro-layout.test.js +150 -0
  115. package/dist/tests/core/profile-pins.test.js +131 -0
  116. package/dist/tests/core/profile-registries.test.js +59 -0
  117. package/dist/tests/core/profile.test.js +110 -0
  118. package/dist/tests/core/registries-capability.test.js +52 -0
  119. package/dist/tests/core/registries-seed.test.js +66 -0
  120. package/dist/tests/core/registries-sync.test.js +205 -0
  121. package/dist/tests/core/registries.test.js +91 -0
  122. package/dist/tests/core/registry-manifest.test.js +95 -0
  123. package/dist/tests/core/registry-versioned-sync.test.js +113 -0
  124. package/dist/tests/core/registry.test.js +51 -0
  125. package/dist/tests/core/skill-integrity.test.js +126 -0
  126. package/dist/tests/core/story-map-parser.test.js +136 -0
  127. package/dist/tests/core/sync-gates.test.js +97 -0
  128. package/dist/tests/core/update-check.test.js +106 -0
  129. package/dist/tests/core/versioning.test.js +169 -0
  130. package/dist/tests/integration/pack-e2e.test.js +50 -0
  131. package/dist/tests/providers/hooks-config.test.js +45 -0
  132. package/dist/tests/providers/index.test.js +58 -0
  133. package/dist/tests/providers/injection-config.test.js +25 -0
  134. package/dist/tests/registry/b3-ledger-wiring.test.js +74 -0
  135. package/dist/tests/registry/catalog-consistency.test.js +47 -0
  136. package/dist/tests/registry/design-skills.test.js +146 -0
  137. package/dist/tests/registry/prose-agnostic.test.js +18 -0
  138. package/dist/tests/registry/sensor-packs.test.js +45 -0
  139. package/dist/tests/registry/skill-versions.test.js +26 -0
  140. package/dist/tests/registry/using-awm.test.js +39 -0
  141. package/dist/tests/utils/config.test.js +31 -0
  142. package/dist/tests/utils/grouping.test.js +43 -0
  143. package/dist/tests/utils/registry-view-overrides.test.js +41 -0
  144. package/dist/tests/utils/registry-view.test.js +156 -0
  145. package/package.json +49 -0
@@ -0,0 +1,217 @@
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.reconcilePack = reconcilePack;
7
+ exports.findManifestDir = findManifestDir;
8
+ exports.runSensors = runSensors;
9
+ const child_process_1 = require("child_process");
10
+ const fs_1 = __importDefault(require("fs"));
11
+ const path_1 = __importDefault(require("path"));
12
+ const tsc_1 = require("./formatters/tsc");
13
+ const eslint_1 = require("./formatters/eslint");
14
+ const semgrep_1 = require("./formatters/semgrep");
15
+ const generic_1 = require("./formatters/generic");
16
+ const test_1 = require("./formatters/test");
17
+ const baseline_1 = require("./baseline");
18
+ const init_1 = require("./init");
19
+ const registries_1 = require("../../core/registries");
20
+ const MANIFEST_FILE = '.awm/sensors.json';
21
+ const DEFAULT_FAST_TIMEOUT = 10_000;
22
+ const DEFAULT_SLOW_TIMEOUT = 120_000;
23
+ // Sensor JSON output can be several MB on large repos (e.g. `eslint --format json`
24
+ // with thousands of findings). execSync defaults to a 1MB buffer and kills the
25
+ // child with SIGTERM when exceeded — which previously surfaced as a false "timeout".
26
+ const MAX_BUFFER = 64 * 1024 * 1024;
27
+ /**
28
+ * Apply the baseline to a sensor result: keep only findings not already accepted.
29
+ * `status` becomes 'pass' when every finding was baseline-suppressed. Skipped
30
+ * sensors are returned untouched.
31
+ */
32
+ function applyBaseline(result, accepted) {
33
+ if (result.status === 'skipped')
34
+ return result;
35
+ const { newErrors, suppressed } = (0, baseline_1.partition)(result.name, result.errors, accepted);
36
+ if (suppressed === 0)
37
+ return result;
38
+ return {
39
+ ...result,
40
+ errors: newErrors,
41
+ status: newErrors.length > 0 ? 'fail' : 'pass',
42
+ newCount: newErrors.length,
43
+ baselineCount: suppressed,
44
+ };
45
+ }
46
+ function readManifest(cwd) {
47
+ const p = path_1.default.join(cwd, MANIFEST_FILE);
48
+ if (!fs_1.default.existsSync(p))
49
+ return null;
50
+ try {
51
+ return JSON.parse(fs_1.default.readFileSync(p, 'utf-8'));
52
+ }
53
+ catch {
54
+ return null;
55
+ }
56
+ }
57
+ /**
58
+ * Upgrade-only, idempotent pack reconciliation. If the manifest sits on the
59
+ * `generic` fallback but the tree now has real stack indicators (package.json,
60
+ * pyproject.toml…), re-detect and rebuild via initSensors — which merges existing
61
+ * custom sensors and copies the pack's config files. Never downgrades, never
62
+ * touches a real pack. FS/registry failures degrade to a no-op (the honest floor
63
+ * in runSensors covers the gap).
64
+ */
65
+ function reconcilePack(manifestDir, manifest, registryRoot) {
66
+ if (manifest.pack !== 'generic') {
67
+ const detection = (0, init_1.detectStack)(manifestDir);
68
+ return { manifest, detection };
69
+ }
70
+ const detection = (0, init_1.detectStack)(manifestDir);
71
+ if (detection.pack === 'generic')
72
+ return { manifest, detection }; // truly generic — stay honest
73
+ const root = registryRoot ?? (0, registries_1.capabilityRoot)('sensor-packs');
74
+ if (!root || !fs_1.default.existsSync(root))
75
+ return { manifest, detection }; // can't rebuild without registry
76
+ try {
77
+ const { manifest: rebuilt } = (0, init_1.initSensors)({ cwd: manifestDir, registryRoot: root, configure: true });
78
+ return { manifest: rebuilt, upgradedFrom: 'generic', detection };
79
+ }
80
+ catch {
81
+ return { manifest, detection }; // never abort the run on a reconcile failure
82
+ }
83
+ }
84
+ /**
85
+ * Walk up from `startCwd` looking for the nearest ancestor that contains
86
+ * `.awm/sensors.json` (git/.git pattern). Returns that directory, or null
87
+ * if none is found before the filesystem root.
88
+ */
89
+ function findManifestDir(startCwd) {
90
+ let dir = path_1.default.resolve(startCwd);
91
+ while (true) {
92
+ if (fs_1.default.existsSync(path_1.default.join(dir, MANIFEST_FILE)))
93
+ return dir;
94
+ const parent = path_1.default.dirname(dir);
95
+ if (parent === dir)
96
+ return null; // reached filesystem root
97
+ dir = parent;
98
+ }
99
+ }
100
+ function shouldRun(isFast, opts) {
101
+ if (opts.all)
102
+ return true;
103
+ if (opts.fast && isFast)
104
+ return true;
105
+ if (opts.slow && !isFast)
106
+ return true;
107
+ if (!opts.fast && !opts.slow && !opts.all)
108
+ return true;
109
+ return false;
110
+ }
111
+ function getFormatter(name) {
112
+ if (name === 'typecheck')
113
+ return tsc_1.parseTscOutput;
114
+ if (name === 'lint')
115
+ return eslint_1.parseEslintOutput;
116
+ if (name === 'security')
117
+ return semgrep_1.parseSemgrepOutput;
118
+ if (name === 'test')
119
+ return test_1.parseTestOutput;
120
+ return generic_1.parseGenericOutput;
121
+ }
122
+ function isExitCodeSensor(name) {
123
+ return name === 'test';
124
+ }
125
+ function runSensor(name, cmd, timeout, cwd) {
126
+ try {
127
+ const raw = (0, child_process_1.execSync)(cmd, { encoding: 'utf-8', timeout, cwd, maxBuffer: MAX_BUFFER, stdio: ['pipe', 'pipe', 'pipe'] });
128
+ const errors = getFormatter(name)(raw);
129
+ return { name, status: errors.length > 0 ? 'fail' : 'pass', errors };
130
+ }
131
+ catch (err) {
132
+ // Output exceeded maxBuffer — child is killed before output can be read.
133
+ // Check this BEFORE the SIGTERM branch (ENOBUFS kills with SIGTERM too).
134
+ if (err.code === 'ENOBUFS') {
135
+ return { name, status: 'skipped', errors: [], skipReason: `output exceeded ${MAX_BUFFER} bytes` };
136
+ }
137
+ // Genuine timeout: execSync kills with SIGTERM after `timeout` ms.
138
+ if (err.code === 'ETIMEDOUT' || (err.killed && err.signal === 'SIGTERM')) {
139
+ return { name, status: 'skipped', errors: [], skipReason: `timeout after ${timeout}ms` };
140
+ }
141
+ // Non-zero exit — the normal path for linters/typecheckers that found
142
+ // findings. Parse the output; if it yields findings, that's a fail.
143
+ const raw = String((err.stdout ?? '') + (err.stderr ?? ''));
144
+ const errors = getFormatter(name)(raw);
145
+ if (errors.length > 0)
146
+ return { name, status: 'fail', errors };
147
+ // A missing tool (binary not installed) must NOT pass silently — the gate
148
+ // cannot certify what it could not run. Treat it as a fail with a clear message.
149
+ const lower = raw.toLowerCase();
150
+ const toolMissing = err.code === 'ENOENT' || // execSync spawn failure (no shell)
151
+ lower.includes('command not found') ||
152
+ lower.includes('enoent') ||
153
+ lower.includes('could not determine executable');
154
+ if (toolMissing) {
155
+ return {
156
+ name,
157
+ status: 'fail',
158
+ errors: [{ message: `sensor tool not available: ${raw.slice(0, 200)}` }],
159
+ };
160
+ }
161
+ // Exit-code sensors (tests): any genuine non-zero exit is a real failure,
162
+ // even when no per-line findings can be parsed from the output.
163
+ if (isExitCodeSensor(name)) {
164
+ return { name, status: 'fail', errors: [{ message: `SENSOR[${name}] failed (exit ${err.status})` }] };
165
+ }
166
+ return { name, status: 'skipped', errors: [], skipReason: `exit ${err.status}: ${raw.slice(0, 200)}` };
167
+ }
168
+ }
169
+ function runSensors(opts = {}) {
170
+ const startCwd = opts.cwd ?? process.cwd();
171
+ const manifestDir = findManifestDir(startCwd);
172
+ if (!manifestDir)
173
+ return { sensors: [], overall: 'not_certified' };
174
+ const manifest = readManifest(manifestDir);
175
+ if (!manifest)
176
+ return { sensors: [], overall: 'not_certified' };
177
+ const reconciled = reconcilePack(manifestDir, manifest);
178
+ const activeManifest = reconciled.manifest;
179
+ const cwd = manifestDir; // ejecutar sensores y baseline desde donde vive el manifest
180
+ const results = [];
181
+ // Baseline suppresses already-accepted findings so sensors fail only on NEW
182
+ // ones (essential on repos with a large pre-existing baseline). Absent file or
183
+ // --ignore-baseline → every finding counts (backward-compatible).
184
+ const baseline = opts.ignoreBaseline ? null : (0, baseline_1.readBaseline)(cwd);
185
+ for (const [name, config] of Object.entries(activeManifest.sensors)) {
186
+ const isFast = config.fast ?? false;
187
+ if (!shouldRun(isFast, opts))
188
+ continue;
189
+ if (config.enabled === false) {
190
+ results.push({ name, status: 'skipped', errors: [], skipReason: 'disabled' });
191
+ continue;
192
+ }
193
+ if (!config.cmd) {
194
+ results.push({ name, status: 'skipped', errors: [], skipReason: 'no cmd configured' });
195
+ continue;
196
+ }
197
+ const timeout = config.timeout ?? (isFast ? DEFAULT_FAST_TIMEOUT : DEFAULT_SLOW_TIMEOUT);
198
+ let result = runSensor(name, config.cmd, timeout, cwd);
199
+ if (baseline)
200
+ result = applyBaseline(result, baseline[name]);
201
+ results.push(result);
202
+ }
203
+ let overall = results.some(r => r.status === 'fail') ? 'fail'
204
+ : results.length > 0 && results.every(r => r.status === 'skipped') ? 'skipped'
205
+ : results.length === 0 ? 'skipped'
206
+ : 'pass';
207
+ // Honest floor: a benign-green 'skipped' over a tree that clearly HAS a stack
208
+ // (indicators present) is a false green — the gate ran nothing real. Never green.
209
+ if (overall === 'skipped' && reconciled.detection.pack !== 'generic') {
210
+ overall = 'not_certified';
211
+ }
212
+ return {
213
+ sensors: results,
214
+ overall,
215
+ ...(reconciled.upgradedFrom ? { packUpgraded: `${reconciled.upgradedFrom}→${activeManifest.pack}` } : {}),
216
+ };
217
+ }
@@ -0,0 +1,85 @@
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.computeSensorStatus = computeSensorStatus;
7
+ const child_process_1 = require("child_process");
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const path_1 = __importDefault(require("path"));
10
+ /** First non-flag token after `npx` — the tool the command actually runs. */
11
+ function npxTool(parts) {
12
+ for (let i = 1; i < parts.length; i++) {
13
+ if (!parts[i].startsWith('-'))
14
+ return parts[i];
15
+ }
16
+ return undefined;
17
+ }
18
+ /** If the command references `--config <file>`, that file must exist in the repo. */
19
+ function configCheck(parts, cwd) {
20
+ const i = parts.indexOf('--config');
21
+ const cfg = i !== -1 ? parts[i + 1] : undefined;
22
+ if (cfg && !fs_1.default.existsSync(path_1.default.join(cwd, cfg))) {
23
+ return { ok: false, detail: `config faltante: ${cfg}` };
24
+ }
25
+ return null;
26
+ }
27
+ /**
28
+ * Verify a sensor command can actually run — not just that `npx` exists.
29
+ * - `npx <tool>`: the tool MUST be installed locally (node_modules/.bin). Otherwise
30
+ * `npx` would fetch a remote package at run time (dependency-confusion risk) and
31
+ * the sensor would fail. A green status here would be a lie.
32
+ * - other binaries: must resolve on PATH (`which`).
33
+ * - any `--config <file>` referenced must exist.
34
+ */
35
+ function checkCmd(cmd, cwd) {
36
+ const parts = cmd.split(/\s+/).filter(Boolean);
37
+ const bin = parts[0];
38
+ if (bin === 'npx') {
39
+ const tool = npxTool(parts);
40
+ if (!tool)
41
+ return { ok: false, detail: 'npx sin tool especificada' };
42
+ const localBin = path_1.default.join(cwd, 'node_modules', '.bin', tool);
43
+ if (!fs_1.default.existsSync(localBin)) {
44
+ return {
45
+ ok: false,
46
+ detail: `${tool} no instalada localmente (npx bajaría un paquete remoto) — agregala a devDependencies`,
47
+ };
48
+ }
49
+ return configCheck(parts, cwd) ?? { ok: true, detail: `${tool} (node_modules/.bin)` };
50
+ }
51
+ try {
52
+ (0, child_process_1.execSync)(`which ${bin}`, { stdio: 'pipe' });
53
+ }
54
+ catch {
55
+ return { ok: false, detail: `${bin} not found in PATH` };
56
+ }
57
+ return configCheck(parts, cwd) ?? { ok: true, detail: bin };
58
+ }
59
+ function computeSensorStatus(cwd = process.cwd()) {
60
+ const manifestPath = path_1.default.join(cwd, '.awm', 'sensors.json');
61
+ if (!fs_1.default.existsSync(manifestPath)) {
62
+ return { overall: 'NOT_CONFIGURED', pack: null, checks: {} };
63
+ }
64
+ let manifest;
65
+ try {
66
+ manifest = JSON.parse(fs_1.default.readFileSync(manifestPath, 'utf-8'));
67
+ }
68
+ catch {
69
+ return { overall: 'NOT_CONFIGURED', pack: null, checks: {} };
70
+ }
71
+ const checks = {};
72
+ for (const [name, config] of Object.entries(manifest.sensors)) {
73
+ if (config.enabled === false) {
74
+ checks[name] = { ok: true, detail: 'disabled' };
75
+ continue;
76
+ }
77
+ if (!config.cmd) {
78
+ checks[name] = { ok: false, detail: 'no cmd configured' };
79
+ continue;
80
+ }
81
+ checks[name] = checkCmd(config.cmd, cwd);
82
+ }
83
+ const allOk = Object.values(checks).every(c => c.ok);
84
+ return { overall: allOk ? 'HEALTHY' : 'DEGRADED', pack: manifest.pack, checks };
85
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,116 @@
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.installBundle = installBundle;
7
+ exports.addBundle = addBundle;
8
+ exports.syncProfile = syncProfile;
9
+ // src/core/bundle-install.ts
10
+ const fs_1 = __importDefault(require("fs"));
11
+ const path_1 = __importDefault(require("path"));
12
+ const bundles_1 = require("./bundles");
13
+ const executor_1 = require("./executor");
14
+ const providers_1 = require("../providers");
15
+ const profile_1 = require("./profile");
16
+ const registries_1 = require("./registries");
17
+ function bundleArtifacts(b, contentDir) {
18
+ const refs = [];
19
+ for (const s of b.skills) {
20
+ refs.push({ name: s.name, type: 'skill', installName: s.name, sourcePath: path_1.default.join(contentDir, 'skills', s.name) });
21
+ }
22
+ for (const w of b.workflows) {
23
+ refs.push({ name: w, type: 'workflow', installName: `${w}.md`, sourcePath: path_1.default.join(contentDir, 'workflows', `${w}.md`) });
24
+ }
25
+ for (const a of b.agents) {
26
+ refs.push({ name: a, type: 'agent', installName: `${a}.md`, sourcePath: path_1.default.join(contentDir, 'agents', `${a}.md`) });
27
+ }
28
+ return refs;
29
+ }
30
+ /**
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).
37
+ */
38
+ function installBundle(opts) {
39
+ const fallbackContentDir = opts.contentDir ?? (0, registries_1.contentRoots)()[0] ?? '';
40
+ 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 };
67
+ }
68
+ /**
69
+ * Installs a bundle (closure) and, when it is a project-scope bundle installed
70
+ * locally, records it as an extension in `.awm/profile.json` and ensures the
71
+ * local symlinks are gitignored. Dependencies are never recorded.
72
+ */
73
+ function addBundle(opts) {
74
+ const summary = installBundle(opts);
75
+ const target = opts.bundles.find((b) => b.name === opts.bundleName);
76
+ 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) {
80
+ const effective = opts.scopeOverride ?? (0, bundles_1.defaultScopeForBundle)(target.scope);
81
+ if ((0, profile_1.shouldRecordExtension)(target.scope, effective)) {
82
+ (0, profile_1.addExtension)(opts.projectRoot, opts.bundleName);
83
+ (0, profile_1.ensureSkillsGitignored)(opts.projectRoot, opts.agents);
84
+ recordedExtension = opts.bundleName;
85
+ }
86
+ }
87
+ return { ...summary, recordedExtension };
88
+ }
89
+ /**
90
+ * Rebuilds local symlinks from `.awm/profile.json` — each listed extension is
91
+ * installed locally (with its dependency closure). Does not modify the profile.
92
+ */
93
+ function syncProfile(opts) {
94
+ const profile = (0, profile_1.readProfile)(opts.projectRoot);
95
+ const installed = [];
96
+ const skipped = [];
97
+ for (const ext of profile.extensions) {
98
+ if (!opts.bundles.some((b) => b.name === ext)) {
99
+ skipped.push(`${ext} (bundle not found in registry — remove with \`awm remove ${ext}\`)`);
100
+ continue;
101
+ }
102
+ const summary = installBundle({
103
+ bundleName: ext,
104
+ bundles: opts.bundles,
105
+ agents: opts.agents,
106
+ method: opts.method,
107
+ projectRoot: opts.projectRoot,
108
+ contentDir: opts.contentDir,
109
+ });
110
+ installed.push(...summary.installed);
111
+ skipped.push(...summary.skipped);
112
+ }
113
+ if (profile.extensions.length > 0)
114
+ (0, profile_1.ensureSkillsGitignored)(opts.projectRoot, opts.agents);
115
+ return { installed, skipped, extensions: profile.extensions };
116
+ }
@@ -0,0 +1,122 @@
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.readCatalog = readCatalog;
7
+ exports.discoverBundles = discoverBundles;
8
+ exports.resolveBundleSkills = resolveBundleSkills;
9
+ exports.defaultScopeForBundle = defaultScopeForBundle;
10
+ exports.discoverAllBundles = discoverAllBundles;
11
+ exports.resolveBundleClosure = resolveBundleClosure;
12
+ const fs_1 = __importDefault(require("fs"));
13
+ const path_1 = __importDefault(require("path"));
14
+ const registries_1 = require("./registries");
15
+ function catalogPath(contentDir) {
16
+ return path_1.default.join(contentDir, 'catalog.json');
17
+ }
18
+ function readCatalog(contentDir) {
19
+ const file = catalogPath(contentDir);
20
+ if (!fs_1.default.existsSync(file))
21
+ return [];
22
+ const parsed = JSON.parse(fs_1.default.readFileSync(file, 'utf-8'));
23
+ return parsed.bundles ?? [];
24
+ }
25
+ function normalizeSkillRefs(raw) {
26
+ return (raw ?? []).map((s) => typeof s === 'string' ? { name: s, onSignal: false } : { name: s.name, onSignal: s.onSignal === true });
27
+ }
28
+ function discoverBundles(contentDir) {
29
+ const entries = readCatalog(contentDir);
30
+ const bundles = [];
31
+ for (const entry of entries) {
32
+ const manifestPath = path_1.default.join(contentDir, entry.source, 'bundle.json');
33
+ if (!fs_1.default.existsSync(manifestPath))
34
+ continue;
35
+ const raw = JSON.parse(fs_1.default.readFileSync(manifestPath, 'utf-8'));
36
+ bundles.push({
37
+ name: raw.name,
38
+ description: raw.description ?? '',
39
+ version: raw.version ?? '0.0.0',
40
+ scope: raw.scope ?? 'project',
41
+ visibility: raw.visibility ?? 'public',
42
+ dependsOn: raw.dependsOn ?? [],
43
+ skills: normalizeSkillRefs(raw.skills),
44
+ workflows: raw.workflows ?? [],
45
+ agents: raw.agents ?? [],
46
+ contentRoot: contentDir,
47
+ });
48
+ }
49
+ return bundles;
50
+ }
51
+ function resolveBundleSkills(bundleName, bundles) {
52
+ const byName = new Map(bundles.map((b) => [b.name, b]));
53
+ const seen = new Set();
54
+ const skills = new Set();
55
+ const visit = (name) => {
56
+ if (seen.has(name))
57
+ return;
58
+ seen.add(name);
59
+ const b = byName.get(name);
60
+ if (!b)
61
+ return;
62
+ for (const dep of b.dependsOn)
63
+ visit(dep);
64
+ for (const s of b.skills)
65
+ skills.add(s.name);
66
+ };
67
+ visit(bundleName);
68
+ return Array.from(skills);
69
+ }
70
+ /**
71
+ * Default install scope for a bundle, derived from its scope class.
72
+ * baseline/ambient install globally; project bundles install locally.
73
+ */
74
+ function defaultScopeForBundle(scope) {
75
+ return scope === 'project' ? 'local' : 'global';
76
+ }
77
+ /** Descubre bundles de TODOS los roots (base + registries adicionales).
78
+ * Colisión de nombre entre roots: override declarado en awm-registry.json
79
+ * del root posterior → reemplaza; no declarado → error nombrando ambas fuentes. */
80
+ function discoverAllBundles(roots = (0, registries_1.contentRoots)()) {
81
+ const byName = new Map();
82
+ for (const root of roots) {
83
+ const overrides = (0, registries_1.readRegistryManifest)(root).overrides;
84
+ for (const b of discoverBundles(root)) {
85
+ const prev = byName.get(b.name);
86
+ if (!prev) {
87
+ byName.set(b.name, b);
88
+ continue;
89
+ }
90
+ if (overrides.has(b.name)) {
91
+ byName.set(b.name, { ...b, overrode: prev.contentRoot });
92
+ continue;
93
+ }
94
+ throw new Error(`Artifact name collision: bundle "${b.name}" exists in both ${prev.contentRoot} and ${root}. ` +
95
+ `Remove or rename one of them, or declare "${b.name}" in "overrides" of the later registry's awm-registry.json.`);
96
+ }
97
+ }
98
+ return Array.from(byName.values());
99
+ }
100
+ /**
101
+ * Resolves the dependency closure of a bundle in deps-first order, deduped.
102
+ * Each bundle appears once, after all bundles it depends on. Unknown names
103
+ * (missing from `bundles`) are skipped.
104
+ */
105
+ function resolveBundleClosure(bundleName, bundles) {
106
+ const byName = new Map(bundles.map((b) => [b.name, b]));
107
+ const ordered = [];
108
+ const seen = new Set();
109
+ const visit = (name) => {
110
+ if (seen.has(name))
111
+ return;
112
+ seen.add(name);
113
+ const b = byName.get(name);
114
+ if (!b)
115
+ return;
116
+ for (const dep of b.dependsOn)
117
+ visit(dep);
118
+ ordered.push(b);
119
+ };
120
+ visit(bundleName);
121
+ return ordered;
122
+ }
@@ -0,0 +1,30 @@
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.CLI_PACKAGE_NAME = void 0;
7
+ exports.cliVersion = cliVersion;
8
+ // src/core/cli-version.ts
9
+ //
10
+ // Versión del propio CLI. Funciona compilado (dist/src/core) y en ts-node
11
+ // (src/core): sube directorios hasta encontrar el package.json del paquete.
12
+ const fs_1 = __importDefault(require("fs"));
13
+ const path_1 = __importDefault(require("path"));
14
+ exports.CLI_PACKAGE_NAME = 'agentic-workflow-manager';
15
+ function cliVersion() {
16
+ let dir = __dirname;
17
+ for (let i = 0; i < 6; i++) {
18
+ const p = path_1.default.join(dir, 'package.json');
19
+ if (fs_1.default.existsSync(p)) {
20
+ try {
21
+ const pkg = JSON.parse(fs_1.default.readFileSync(p, 'utf-8'));
22
+ if (pkg.name === exports.CLI_PACKAGE_NAME && typeof pkg.version === 'string')
23
+ return pkg.version;
24
+ }
25
+ catch { /* package.json ajeno o ilegible — seguir subiendo */ }
26
+ }
27
+ dir = path_1.default.dirname(dir);
28
+ }
29
+ return '0.0.0';
30
+ }
@@ -0,0 +1,30 @@
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.globalContextPath = globalContextPath;
7
+ exports.materialize = materialize;
8
+ // cli/src/core/context/materializer.ts
9
+ const fs_1 = __importDefault(require("fs"));
10
+ const os_1 = __importDefault(require("os"));
11
+ const path_1 = __importDefault(require("path"));
12
+ const provider_1 = require("./provider");
13
+ function awmHome() {
14
+ return process.env.AWM_HOME || path_1.default.join(process.env.HOME || os_1.default.homedir(), '.awm');
15
+ }
16
+ function globalContextPath() {
17
+ return path_1.default.join(awmHome(), 'context', 'awm-context.md');
18
+ }
19
+ function materialize(ctx, absPath, scope) {
20
+ let onDisk = null;
21
+ try {
22
+ onDisk = (0, provider_1.sha256)(fs_1.default.readFileSync(absPath, 'utf-8'));
23
+ }
24
+ catch { /* file absent or removed */ }
25
+ if (onDisk !== ctx.contentHash) {
26
+ fs_1.default.mkdirSync(path_1.default.dirname(absPath), { recursive: true });
27
+ fs_1.default.writeFileSync(absPath, ctx.markdown, 'utf-8');
28
+ }
29
+ return { absPath, scope, contentHash: ctx.contentHash };
30
+ }
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.InjectionOrchestrator = void 0;
4
+ // cli/src/core/context/orchestrator.ts
5
+ const providers_1 = require("../../providers");
6
+ const hook_merge_1 = require("./strategies/hook-merge");
7
+ const config_instructions_1 = require("./strategies/config-instructions");
8
+ const provider_1 = require("./provider");
9
+ const materializer_1 = require("./materializer");
10
+ class InjectionOrchestrator {
11
+ overrides;
12
+ constructor(overrides = {}) {
13
+ this.overrides = overrides;
14
+ }
15
+ provider(agent) {
16
+ return this.overrides.providerOverride ?? providers_1.PROVIDERS[agent];
17
+ }
18
+ strategy(agent) {
19
+ const inj = this.overrides.providerOverride !== undefined
20
+ ? this.overrides.providerOverride.injection
21
+ : (0, providers_1.getInjection)(agent);
22
+ if (!inj)
23
+ throw new Error(`agent '${agent}' has no injection mechanism configured`);
24
+ switch (inj.type) {
25
+ case 'cc-settings-merge': return new hook_merge_1.HookMergeStrategy();
26
+ case 'config-instructions': return new config_instructions_1.ConfigInstructionsStrategy();
27
+ }
28
+ }
29
+ /** Full input: builds context from registry and materializes to disk. Used by installContext only. */
30
+ inputFor(op) {
31
+ const ctx = (0, provider_1.buildContext)({ registryRoot: op.registryRoot, profileExtensions: op.profileExtensions });
32
+ const absPath = this.overrides.contextPathOverride ?? (0, materializer_1.globalContextPath)();
33
+ const ref = (0, materializer_1.materialize)(ctx, absPath, op.scope);
34
+ return { ref, registryRoot: op.registryRoot, installMethod: op.installMethod, agent: op.agent, scope: op.scope };
35
+ }
36
+ /** Path-only input: no buildContext, no materialize. Safe for remove() which never reads contentHash. */
37
+ pathInputFor(op) {
38
+ const absPath = this.overrides.contextPathOverride ?? (0, materializer_1.globalContextPath)();
39
+ const ref = { absPath, scope: op.scope, contentHash: '' };
40
+ return { ref, registryRoot: op.registryRoot, installMethod: op.installMethod, agent: op.agent, scope: op.scope };
41
+ }
42
+ /**
43
+ * Status input: builds context from registry (to get expected hash) but does NOT materialize.
44
+ * Avoids silently correcting a stale file before the strategy can observe it.
45
+ */
46
+ statusInputFor(op) {
47
+ const absPath = this.overrides.contextPathOverride ?? (0, materializer_1.globalContextPath)();
48
+ let contentHash = '';
49
+ try {
50
+ const ctx = (0, provider_1.buildContext)({ registryRoot: op.registryRoot, profileExtensions: op.profileExtensions });
51
+ contentHash = ctx.contentHash;
52
+ }
53
+ catch (err) {
54
+ // Only suppress "registry not yet initialised" — all other errors propagate.
55
+ const msg = err instanceof Error ? err.message : String(err);
56
+ if (!msg.includes('using-awm skill not found'))
57
+ throw err;
58
+ }
59
+ const ref = { absPath, scope: op.scope, contentHash };
60
+ return { ref, registryRoot: op.registryRoot, installMethod: op.installMethod, agent: op.agent, scope: op.scope };
61
+ }
62
+ installContext(op) {
63
+ const provider = this.provider(op.agent);
64
+ this.strategy(op.agent).inject(this.inputFor(op), provider);
65
+ }
66
+ uninstallContext(op) {
67
+ const provider = this.provider(op.agent);
68
+ this.strategy(op.agent).remove(this.pathInputFor(op), provider);
69
+ }
70
+ contextStatus(op) {
71
+ const provider = this.provider(op.agent);
72
+ return this.strategy(op.agent).status(this.statusInputFor(op), provider);
73
+ }
74
+ }
75
+ exports.InjectionOrchestrator = InjectionOrchestrator;