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.
- package/README.md +62 -0
- package/dist/src/commands/doctor.js +77 -0
- package/dist/src/commands/hooks/index.js +118 -0
- package/dist/src/commands/hooks/install.js +113 -0
- package/dist/src/commands/hooks/resync.js +50 -0
- package/dist/src/commands/hooks/status.js +74 -0
- package/dist/src/commands/hooks/uninstall.js +59 -0
- package/dist/src/commands/init.js +181 -0
- package/dist/src/commands/ledger/index.js +73 -0
- package/dist/src/commands/pin.js +75 -0
- package/dist/src/commands/registry/add.js +62 -0
- package/dist/src/commands/registry/index.js +166 -0
- package/dist/src/commands/registry/install-bundles.js +36 -0
- package/dist/src/commands/registry/remove.js +18 -0
- package/dist/src/commands/registry/status.js +31 -0
- package/dist/src/commands/sensors/baseline.js +93 -0
- package/dist/src/commands/sensors/formatters/eslint.js +29 -0
- package/dist/src/commands/sensors/formatters/generic.js +8 -0
- package/dist/src/commands/sensors/formatters/semgrep.js +18 -0
- package/dist/src/commands/sensors/formatters/test.js +12 -0
- package/dist/src/commands/sensors/formatters/tsc.js +23 -0
- package/dist/src/commands/sensors/index.js +94 -0
- package/dist/src/commands/sensors/init.js +112 -0
- package/dist/src/commands/sensors/install.js +78 -0
- package/dist/src/commands/sensors/run.js +217 -0
- package/dist/src/commands/sensors/status.js +85 -0
- package/dist/src/commands/sensors/types.js +2 -0
- package/dist/src/core/bundle-install.js +116 -0
- package/dist/src/core/bundles.js +122 -0
- package/dist/src/core/cli-version.js +30 -0
- package/dist/src/core/context/materializer.js +30 -0
- package/dist/src/core/context/orchestrator.js +75 -0
- package/dist/src/core/context/project-constitution-inject.js +47 -0
- package/dist/src/core/context/provider.js +29 -0
- package/dist/src/core/context/regenerate.js +61 -0
- package/dist/src/core/context/strategies/config-instructions.js +73 -0
- package/dist/src/core/context/strategies/hook-merge.js +23 -0
- package/dist/src/core/context/strategies/strategy.js +2 -0
- package/dist/src/core/context/types.js +2 -0
- package/dist/src/core/diagnostics/checks.js +141 -0
- package/dist/src/core/diagnostics/context.js +181 -0
- package/dist/src/core/diagnostics/types.js +2 -0
- package/dist/src/core/discovery.js +135 -0
- package/dist/src/core/executor.js +41 -0
- package/dist/src/core/init/detector.js +67 -0
- package/dist/src/core/init/orchestrator.js +54 -0
- package/dist/src/core/init/steps.js +279 -0
- package/dist/src/core/init/types.js +2 -0
- package/dist/src/core/ledger/store.js +73 -0
- package/dist/src/core/ledger/types.js +2 -0
- package/dist/src/core/miro.js +314 -0
- package/dist/src/core/profile-pins.js +36 -0
- package/dist/src/core/profile.js +146 -0
- package/dist/src/core/registries.js +205 -0
- package/dist/src/core/registry.js +28 -0
- package/dist/src/core/skill-integrity.js +97 -0
- package/dist/src/core/story-map-parser.js +83 -0
- package/dist/src/core/update-check-worker.js +10 -0
- package/dist/src/core/update-check.js +106 -0
- package/dist/src/core/versioning.js +112 -0
- package/dist/src/index.js +689 -0
- package/dist/src/providers/index.js +63 -0
- package/dist/src/utils/config.js +35 -0
- package/dist/src/utils/grouping.js +51 -0
- package/dist/src/utils/registry-view.js +154 -0
- package/dist/tests/commands/doctor.test.js +98 -0
- package/dist/tests/commands/hooks/install.test.js +149 -0
- package/dist/tests/commands/hooks/resync.test.js +135 -0
- package/dist/tests/commands/hooks/router.test.js +26 -0
- package/dist/tests/commands/hooks/status.test.js +88 -0
- package/dist/tests/commands/hooks/uninstall.test.js +104 -0
- package/dist/tests/commands/init.test.js +87 -0
- package/dist/tests/commands/ledger/index.test.js +64 -0
- package/dist/tests/commands/pin.test.js +72 -0
- package/dist/tests/commands/registry/add.test.js +223 -0
- package/dist/tests/commands/registry/install-bundles.test.js +87 -0
- package/dist/tests/commands/registry/remove.test.js +49 -0
- package/dist/tests/commands/registry/status.test.js +43 -0
- package/dist/tests/commands/sensors/baseline.test.js +106 -0
- package/dist/tests/commands/sensors/formatters/eslint.test.js +32 -0
- package/dist/tests/commands/sensors/formatters/semgrep.test.js +38 -0
- package/dist/tests/commands/sensors/formatters/tsc.test.js +25 -0
- package/dist/tests/commands/sensors/index.test.js +18 -0
- package/dist/tests/commands/sensors/init.test.js +137 -0
- package/dist/tests/commands/sensors/install.test.js +60 -0
- package/dist/tests/commands/sensors/router.test.js +23 -0
- package/dist/tests/commands/sensors/run.test.js +353 -0
- package/dist/tests/commands/sensors/status.test.js +98 -0
- package/dist/tests/core/base-remote.test.js +70 -0
- package/dist/tests/core/bundle-install.test.js +198 -0
- package/dist/tests/core/bundles-multiroot.test.js +68 -0
- package/dist/tests/core/bundles-overrides.test.js +49 -0
- package/dist/tests/core/bundles.test.js +96 -0
- package/dist/tests/core/cli-version.test.js +17 -0
- package/dist/tests/core/context/materializer.test.js +46 -0
- package/dist/tests/core/context/orchestrator.test.js +127 -0
- package/dist/tests/core/context/project-constitution-inject.test.js +82 -0
- package/dist/tests/core/context/provider.test.js +45 -0
- package/dist/tests/core/context/regenerate.test.js +106 -0
- package/dist/tests/core/context/strategies/config-instructions.test.js +88 -0
- package/dist/tests/core/context/strategies/hook-merge.test.js +71 -0
- package/dist/tests/core/context/types.test.js +15 -0
- package/dist/tests/core/diagnostics/checks.test.js +208 -0
- package/dist/tests/core/diagnostics/context.test.js +170 -0
- package/dist/tests/core/discovery-multiroot.test.js +63 -0
- package/dist/tests/core/discovery-overrides.test.js +105 -0
- package/dist/tests/core/discovery.test.js +113 -0
- package/dist/tests/core/executor.test.js +44 -0
- package/dist/tests/core/init/detector.test.js +56 -0
- package/dist/tests/core/init/orchestrator.test.js +122 -0
- package/dist/tests/core/init/steps.test.js +363 -0
- package/dist/tests/core/ledger/gitignore.test.js +13 -0
- package/dist/tests/core/ledger/store.test.js +122 -0
- package/dist/tests/core/miro-layout.test.js +150 -0
- package/dist/tests/core/profile-pins.test.js +131 -0
- package/dist/tests/core/profile-registries.test.js +59 -0
- package/dist/tests/core/profile.test.js +110 -0
- package/dist/tests/core/registries-capability.test.js +52 -0
- package/dist/tests/core/registries-seed.test.js +66 -0
- package/dist/tests/core/registries-sync.test.js +205 -0
- package/dist/tests/core/registries.test.js +91 -0
- package/dist/tests/core/registry-manifest.test.js +95 -0
- package/dist/tests/core/registry-versioned-sync.test.js +113 -0
- package/dist/tests/core/registry.test.js +51 -0
- package/dist/tests/core/skill-integrity.test.js +126 -0
- package/dist/tests/core/story-map-parser.test.js +136 -0
- package/dist/tests/core/sync-gates.test.js +97 -0
- package/dist/tests/core/update-check.test.js +106 -0
- package/dist/tests/core/versioning.test.js +169 -0
- package/dist/tests/integration/pack-e2e.test.js +50 -0
- package/dist/tests/providers/hooks-config.test.js +45 -0
- package/dist/tests/providers/index.test.js +58 -0
- package/dist/tests/providers/injection-config.test.js +25 -0
- package/dist/tests/registry/b3-ledger-wiring.test.js +74 -0
- package/dist/tests/registry/catalog-consistency.test.js +47 -0
- package/dist/tests/registry/design-skills.test.js +146 -0
- package/dist/tests/registry/prose-agnostic.test.js +18 -0
- package/dist/tests/registry/sensor-packs.test.js +45 -0
- package/dist/tests/registry/skill-versions.test.js +26 -0
- package/dist/tests/registry/using-awm.test.js +39 -0
- package/dist/tests/utils/config.test.js +31 -0
- package/dist/tests/utils/grouping.test.js +43 -0
- package/dist/tests/utils/registry-view-overrides.test.js +41 -0
- package/dist/tests/utils/registry-view.test.js +156 -0
- package/package.json +49 -0
|
@@ -0,0 +1,47 @@
|
|
|
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.injectProjectConstitution = injectProjectConstitution;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const providers_1 = require("../../providers");
|
|
10
|
+
/**
|
|
11
|
+
* Entrega el `CONSTITUTION.md` del proyecto a agentes cuyo mecanismo de contexto es
|
|
12
|
+
* `config-instructions` (hoy OpenCode), agregando una entrada relativa
|
|
13
|
+
* `CONSTITUTION.md` al `instructions[]` de un `opencode.json` en la raíz del
|
|
14
|
+
* proyecto (commiteable, viaja con el repo). Claude lo recibe vía el hook
|
|
15
|
+
* SessionStart, así que para Claude es no-op. Agnóstico por construcción:
|
|
16
|
+
* cualquier agente futuro con inyección `config-instructions` hereda el trato.
|
|
17
|
+
*/
|
|
18
|
+
function injectProjectConstitution(projectRoot, agent) {
|
|
19
|
+
const inj = providers_1.PROVIDERS[agent].injection;
|
|
20
|
+
if (!inj || inj.type !== 'config-instructions')
|
|
21
|
+
return 'not-applicable';
|
|
22
|
+
if (!fs_1.default.existsSync(path_1.default.join(projectRoot, 'CONSTITUTION.md')))
|
|
23
|
+
return 'no-constitution';
|
|
24
|
+
const configPath = path_1.default.join(projectRoot, path_1.default.basename(inj.configPath)); // p.ej. 'opencode.json'
|
|
25
|
+
const field = inj.field; // 'instructions'
|
|
26
|
+
const REF = 'CONSTITUTION.md';
|
|
27
|
+
let cfg = { $schema: 'https://opencode.ai/config.json' };
|
|
28
|
+
if (fs_1.default.existsSync(configPath)) {
|
|
29
|
+
try {
|
|
30
|
+
cfg = JSON.parse(fs_1.default.readFileSync(configPath, 'utf-8'));
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
throw new Error(`${configPath} is not valid JSON. Fix it manually, then re-run.`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const current = cfg[field];
|
|
37
|
+
if (current !== undefined && !Array.isArray(current)) {
|
|
38
|
+
throw new Error(`${configPath}: '${field}' field must be an array. Fix it manually, then re-run.`);
|
|
39
|
+
}
|
|
40
|
+
const list = Array.isArray(current) ? current : [];
|
|
41
|
+
if (list.includes(REF))
|
|
42
|
+
return 'already';
|
|
43
|
+
list.push(REF);
|
|
44
|
+
cfg[field] = list;
|
|
45
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(cfg, null, 2) + '\n', 'utf-8');
|
|
46
|
+
return 'injected';
|
|
47
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
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.sha256 = sha256;
|
|
7
|
+
exports.buildContext = buildContext;
|
|
8
|
+
// cli/src/core/context/provider.ts
|
|
9
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
10
|
+
const fs_1 = __importDefault(require("fs"));
|
|
11
|
+
const path_1 = __importDefault(require("path"));
|
|
12
|
+
function sha256(input) {
|
|
13
|
+
return crypto_1.default.createHash('sha256').update(input, 'utf-8').digest('hex');
|
|
14
|
+
}
|
|
15
|
+
function parseVersion(skill) {
|
|
16
|
+
const m = skill.match(/^version:\s*["']?([^"'\n]+)["']?\s*$/m);
|
|
17
|
+
return m ? m[1].trim() : '0.0.0';
|
|
18
|
+
}
|
|
19
|
+
function buildContext(input) {
|
|
20
|
+
const skillPath = path_1.default.join(input.registryRoot, 'skills/using-awm/SKILL.md');
|
|
21
|
+
if (!fs_1.default.existsSync(skillPath)) {
|
|
22
|
+
throw new Error(`using-awm skill not found at ${skillPath}. Run 'awm update' first.`);
|
|
23
|
+
}
|
|
24
|
+
const skill = fs_1.default.readFileSync(skillPath, 'utf-8');
|
|
25
|
+
const exts = input.profileExtensions.length ? input.profileExtensions.join(', ') : 'ninguna';
|
|
26
|
+
const header = `<!-- AWM context (generated) -->\n# AWM\n\nExtensiones activas: ${exts}\n\n`;
|
|
27
|
+
const markdown = header + skill;
|
|
28
|
+
return { markdown, sourceVersion: parseVersion(skill), contentHash: sha256(markdown) };
|
|
29
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
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.regenerateGlobalContext = regenerateGlobalContext;
|
|
7
|
+
// W3 — Regeneración del contexto global tras `awm update`.
|
|
8
|
+
// Para cada agente con inyección config-instructions cuya config exista:
|
|
9
|
+
// - 'stale' (sentinel presente, archivo materializado ausente/viejo) → re-materializa.
|
|
10
|
+
// - 'injected' (ya fresco) → no toca nada.
|
|
11
|
+
// - 'absent' (sentinel ausente) → no inyecta (eso es `awm init`).
|
|
12
|
+
// Solo scope global. Defensivo: nunca rompe `awm update` por una falla de un agente.
|
|
13
|
+
const fs_1 = __importDefault(require("fs"));
|
|
14
|
+
const providers_1 = require("../../providers");
|
|
15
|
+
const registries_1 = require("../registries");
|
|
16
|
+
const orchestrator_1 = require("./orchestrator");
|
|
17
|
+
function regenerateGlobalContext(orch = new orchestrator_1.InjectionOrchestrator()) {
|
|
18
|
+
const skillsRoot = (0, registries_1.capabilityRoot)('skills');
|
|
19
|
+
if (!skillsRoot)
|
|
20
|
+
return [];
|
|
21
|
+
const out = [];
|
|
22
|
+
for (const agent of Object.keys(providers_1.PROVIDERS)) {
|
|
23
|
+
const inj = providers_1.PROVIDERS[agent].injection;
|
|
24
|
+
if (!inj || inj.type !== 'config-instructions')
|
|
25
|
+
continue;
|
|
26
|
+
if (!fs_1.default.existsSync(inj.configPath))
|
|
27
|
+
continue;
|
|
28
|
+
const op = {
|
|
29
|
+
agent,
|
|
30
|
+
scope: 'global',
|
|
31
|
+
registryRoot: skillsRoot,
|
|
32
|
+
installMethod: 'symlink', // config-instructions strategy ignores this; consistent with stepContextInjection/gatherContextInjection
|
|
33
|
+
profileExtensions: [],
|
|
34
|
+
};
|
|
35
|
+
let state;
|
|
36
|
+
try {
|
|
37
|
+
state = orch.contextStatus(op);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
out.push({ agent, action: 'skipped' });
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (state === 'injected') {
|
|
44
|
+
out.push({ agent, action: 'fresh' });
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (state === 'absent') {
|
|
48
|
+
out.push({ agent, action: 'skipped' });
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
// state === 'stale'
|
|
52
|
+
try {
|
|
53
|
+
orch.installContext(op);
|
|
54
|
+
out.push({ agent, action: 'refreshed' });
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
out.push({ agent, action: 'skipped' });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return out;
|
|
61
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ConfigInstructionsStrategy = void 0;
|
|
7
|
+
// cli/src/core/context/strategies/config-instructions.ts
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const provider_1 = require("../provider");
|
|
11
|
+
class ConfigInstructionsStrategy {
|
|
12
|
+
cfgOf(provider) {
|
|
13
|
+
const inj = provider.injection;
|
|
14
|
+
if (!inj || inj.type !== 'config-instructions') {
|
|
15
|
+
throw new Error('ConfigInstructionsStrategy requires a config-instructions provider');
|
|
16
|
+
}
|
|
17
|
+
return { configPath: inj.configPath, field: inj.field };
|
|
18
|
+
}
|
|
19
|
+
read(configPath) {
|
|
20
|
+
if (!fs_1.default.existsSync(configPath))
|
|
21
|
+
return { $schema: 'https://opencode.ai/config.json', instructions: [] };
|
|
22
|
+
const raw = fs_1.default.readFileSync(configPath, 'utf-8');
|
|
23
|
+
try {
|
|
24
|
+
return JSON.parse(raw);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
throw new Error(`${configPath} is not valid JSON. Fix it manually, then re-run.`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
write(configPath, cfg) {
|
|
31
|
+
fs_1.default.mkdirSync(path_1.default.dirname(configPath), { recursive: true });
|
|
32
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(cfg, null, 2) + '\n', 'utf-8');
|
|
33
|
+
}
|
|
34
|
+
inject(input, provider) {
|
|
35
|
+
const { configPath, field } = this.cfgOf(provider);
|
|
36
|
+
const cfg = this.read(configPath);
|
|
37
|
+
const current = cfg[field];
|
|
38
|
+
if (current !== undefined && !Array.isArray(current)) {
|
|
39
|
+
throw new Error(`${configPath}: '${field}' field must be an array. Fix it manually, then re-run.`);
|
|
40
|
+
}
|
|
41
|
+
const list = Array.isArray(current) ? current : [];
|
|
42
|
+
if (!list.includes(input.ref.absPath))
|
|
43
|
+
list.push(input.ref.absPath);
|
|
44
|
+
cfg[field] = list;
|
|
45
|
+
this.write(configPath, cfg);
|
|
46
|
+
}
|
|
47
|
+
remove(input, provider) {
|
|
48
|
+
const { configPath, field } = this.cfgOf(provider);
|
|
49
|
+
if (!fs_1.default.existsSync(configPath))
|
|
50
|
+
return;
|
|
51
|
+
const cfg = this.read(configPath);
|
|
52
|
+
const current = cfg[field];
|
|
53
|
+
if (current !== undefined && !Array.isArray(current)) {
|
|
54
|
+
throw new Error(`${configPath}: '${field}' field must be an array. Fix it manually, then re-run.`);
|
|
55
|
+
}
|
|
56
|
+
cfg[field] = (Array.isArray(current) ? current : []).filter((e) => e !== input.ref.absPath);
|
|
57
|
+
this.write(configPath, cfg);
|
|
58
|
+
}
|
|
59
|
+
status(input, provider) {
|
|
60
|
+
const { configPath, field } = this.cfgOf(provider);
|
|
61
|
+
if (!fs_1.default.existsSync(configPath))
|
|
62
|
+
return 'absent';
|
|
63
|
+
const cfg = this.read(configPath);
|
|
64
|
+
const list = cfg[field];
|
|
65
|
+
if (!Array.isArray(list) || !list.includes(input.ref.absPath))
|
|
66
|
+
return 'absent';
|
|
67
|
+
if (!fs_1.default.existsSync(input.ref.absPath))
|
|
68
|
+
return 'stale';
|
|
69
|
+
const onDisk = (0, provider_1.sha256)(fs_1.default.readFileSync(input.ref.absPath, 'utf-8'));
|
|
70
|
+
return onDisk === input.ref.contentHash ? 'injected' : 'stale';
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
exports.ConfigInstructionsStrategy = ConfigInstructionsStrategy;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HookMergeStrategy = void 0;
|
|
4
|
+
const install_1 = require("../../../commands/hooks/install");
|
|
5
|
+
const uninstall_1 = require("../../../commands/hooks/uninstall");
|
|
6
|
+
const status_1 = require("../../../commands/hooks/status");
|
|
7
|
+
const STATE_BY_OVERALL = {
|
|
8
|
+
HEALTHY: 'injected',
|
|
9
|
+
DEGRADED: 'stale',
|
|
10
|
+
NOT_INSTALLED: 'absent',
|
|
11
|
+
};
|
|
12
|
+
class HookMergeStrategy {
|
|
13
|
+
inject(input, _provider) {
|
|
14
|
+
(0, install_1.installHook)({ agent: input.agent, registryRoot: input.registryRoot, installMethod: input.installMethod });
|
|
15
|
+
}
|
|
16
|
+
remove(input, _provider) {
|
|
17
|
+
(0, uninstall_1.uninstallHook)({ agent: input.agent });
|
|
18
|
+
}
|
|
19
|
+
status(input, _provider) {
|
|
20
|
+
return STATE_BY_OVERALL[(0, status_1.computeHookStatus)(input.agent).overall] ?? 'absent';
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
exports.HookMergeStrategy = HookMergeStrategy;
|
|
@@ -0,0 +1,141 @@
|
|
|
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.runChecks = runChecks;
|
|
7
|
+
// src/core/diagnostics/checks.ts
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const cmd = (value) => ({ kind: 'command', value });
|
|
10
|
+
const skillRemedy = (value) => ({ kind: 'skill', value });
|
|
11
|
+
const none = { kind: 'none' };
|
|
12
|
+
function machineChecks(m) {
|
|
13
|
+
const out = [];
|
|
14
|
+
// machine.cli
|
|
15
|
+
if (!m.registryCache.present) {
|
|
16
|
+
out.push({ id: 'machine.cli', level: 'machine', label: 'CLI', status: 'missing',
|
|
17
|
+
detail: 'registry cache missing — run awm init', remedy: cmd('awm init') });
|
|
18
|
+
}
|
|
19
|
+
else if (m.registryCache.gitState === 'clean') {
|
|
20
|
+
out.push({ id: 'machine.cli', level: 'machine', label: 'CLI', status: 'ok', remedy: none });
|
|
21
|
+
}
|
|
22
|
+
else if (m.registryCache.gitState === 'behind') {
|
|
23
|
+
out.push({ id: 'machine.cli', level: 'machine', label: 'CLI', status: 'warn',
|
|
24
|
+
detail: 'cache desactualizado', remedy: cmd('awm update') });
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
// dirty | unknown | undefined → advisory, sin acción
|
|
28
|
+
out.push({ id: 'machine.cli', level: 'machine', label: 'CLI', status: 'warn',
|
|
29
|
+
detail: `git ${m.registryCache.gitState ?? 'unknown'}`, remedy: none });
|
|
30
|
+
}
|
|
31
|
+
// machine.hook
|
|
32
|
+
if (m.hook.present && !m.hook.degraded) {
|
|
33
|
+
out.push({ id: 'machine.hook', level: 'machine', label: 'hook SessionStart', status: 'ok', remedy: none });
|
|
34
|
+
}
|
|
35
|
+
else if (m.hook.present) {
|
|
36
|
+
out.push({ id: 'machine.hook', level: 'machine', label: 'hook SessionStart', status: 'warn',
|
|
37
|
+
detail: 'scripts incompletos', remedy: cmd('awm init') });
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
out.push({ id: 'machine.hook', level: 'machine', label: 'hook SessionStart', status: 'missing',
|
|
41
|
+
remedy: cmd('awm init') });
|
|
42
|
+
}
|
|
43
|
+
// machine.devCore
|
|
44
|
+
if (m.devCore.present && m.devCore.brokenLinks.length === 0) {
|
|
45
|
+
out.push({ id: 'machine.devCore', level: 'machine', label: 'dev-core (baseline)', status: 'ok', remedy: none });
|
|
46
|
+
}
|
|
47
|
+
else if (m.devCore.present) {
|
|
48
|
+
out.push({ id: 'machine.devCore', level: 'machine', label: 'dev-core (baseline)', status: 'warn',
|
|
49
|
+
detail: `${m.devCore.brokenLinks.length} symlinks rotos`, remedy: cmd('awm init') });
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
out.push({ id: 'machine.devCore', level: 'machine', label: 'dev-core (baseline)', status: 'missing',
|
|
53
|
+
remedy: cmd('awm init') });
|
|
54
|
+
}
|
|
55
|
+
// machine.globalSkills — integridad de symlinks en ~/.claude/skills (fuera del baseline)
|
|
56
|
+
const brokenGlobal = m.globalSkills.repairable.length + m.globalSkills.dead.length;
|
|
57
|
+
if (brokenGlobal === 0) {
|
|
58
|
+
out.push({ id: 'machine.globalSkills', level: 'machine', label: 'skills globales', status: 'ok', remedy: none });
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
out.push({ id: 'machine.globalSkills', level: 'machine', label: 'skills globales', status: 'warn',
|
|
62
|
+
detail: `${brokenGlobal} enlaces rotos`, remedy: cmd('awm init') });
|
|
63
|
+
}
|
|
64
|
+
// machine.ambient.<b> — una fila por bundle deseado
|
|
65
|
+
for (const b of m.ambient.wanted) {
|
|
66
|
+
const installed = m.ambient.installed.includes(b);
|
|
67
|
+
out.push({ id: `machine.ambient.${b}`, level: 'machine', label: `${b} (ambient)`,
|
|
68
|
+
status: installed ? 'ok' : 'missing', remedy: installed ? none : cmd(`awm add ${b}`) });
|
|
69
|
+
}
|
|
70
|
+
// machine.context.<agent> — una fila por agente con contexto AWM gestionado
|
|
71
|
+
for (const c of m.contextInjection) {
|
|
72
|
+
if (c.state === 'injected') {
|
|
73
|
+
out.push({ id: `machine.context.${c.agent}`, level: 'machine', label: `contexto AWM (${c.agent})`,
|
|
74
|
+
status: 'ok', remedy: none });
|
|
75
|
+
}
|
|
76
|
+
else if (c.state === 'stale') {
|
|
77
|
+
out.push({ id: `machine.context.${c.agent}`, level: 'machine', label: `contexto AWM (${c.agent})`,
|
|
78
|
+
status: 'warn', detail: 'contexto desactualizado', remedy: cmd('awm init') });
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
out.push({ id: `machine.context.${c.agent}`, level: 'machine', label: `contexto AWM (${c.agent})`,
|
|
82
|
+
status: 'missing', remedy: cmd('awm init') });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return out;
|
|
86
|
+
}
|
|
87
|
+
function projectChecks(p) {
|
|
88
|
+
const out = [];
|
|
89
|
+
// project.profile
|
|
90
|
+
if (p.profile.present) {
|
|
91
|
+
const exts = p.profile.extensions.length ? p.profile.extensions.join(', ') : 'sin extensiones';
|
|
92
|
+
out.push({ id: 'project.profile', level: 'project', label: `.awm/profile.json (${exts})`,
|
|
93
|
+
status: 'ok', remedy: none });
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
out.push({ id: 'project.profile', level: 'project', label: '.awm/profile.json', status: 'missing',
|
|
97
|
+
remedy: cmd('awm init') });
|
|
98
|
+
}
|
|
99
|
+
// project.activation
|
|
100
|
+
const missingLinks = p.activeBundles.expected.filter((s) => !p.activeBundles.linked.includes(s));
|
|
101
|
+
if (p.activeBundles.broken.length === 0 && missingLinks.length === 0) {
|
|
102
|
+
out.push({ id: 'project.activation', level: 'project', label: 'bundles activos', status: 'ok', remedy: none });
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
out.push({ id: 'project.activation', level: 'project', label: 'bundles activos', status: 'missing',
|
|
106
|
+
detail: `${missingLinks.length} faltan, ${p.activeBundles.broken.length} rotos`, remedy: cmd('awm sync') });
|
|
107
|
+
}
|
|
108
|
+
// project.sensors
|
|
109
|
+
out.push(p.sensors.present
|
|
110
|
+
? { id: 'project.sensors', level: 'project', label: 'sensores', status: 'ok', remedy: none }
|
|
111
|
+
: { id: 'project.sensors', level: 'project', label: 'sensores no inicializados', status: 'missing',
|
|
112
|
+
remedy: cmd('awm sensors init') });
|
|
113
|
+
// project.constitution (missing degrada; remedio agente)
|
|
114
|
+
out.push(p.constitution.present
|
|
115
|
+
? { id: 'project.constitution', level: 'project', label: 'CONSTITUTION.md', status: 'ok', remedy: none }
|
|
116
|
+
: { id: 'project.constitution', level: 'project', label: 'CONSTITUTION.md ausente', status: 'missing',
|
|
117
|
+
remedy: skillRemedy('project-constitution') });
|
|
118
|
+
// project.context (advisory; no degrada)
|
|
119
|
+
if (p.context.present) {
|
|
120
|
+
out.push({ id: 'project.context', level: 'project', label: p.context.file ?? 'CLAUDE.md',
|
|
121
|
+
status: 'ok', remedy: none });
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
out.push({ id: 'project.context', level: 'project', label: 'contexto del agente (CLAUDE.md/AGENTS.md) ausente', status: 'warn',
|
|
125
|
+
remedy: skillRemedy('project-context-init') });
|
|
126
|
+
}
|
|
127
|
+
return out;
|
|
128
|
+
}
|
|
129
|
+
function runChecks(ctx) {
|
|
130
|
+
const results = [
|
|
131
|
+
...machineChecks(ctx.machine),
|
|
132
|
+
...(ctx.project ? projectChecks(ctx.project) : []),
|
|
133
|
+
];
|
|
134
|
+
const overall = results.some((r) => r.status === 'missing') ? 'degraded' : 'healthy';
|
|
135
|
+
return {
|
|
136
|
+
results,
|
|
137
|
+
overall,
|
|
138
|
+
hasProject: ctx.project !== null,
|
|
139
|
+
projectName: ctx.project ? path_1.default.basename(ctx.project.root) : undefined,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
@@ -0,0 +1,181 @@
|
|
|
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.gatherContext = gatherContext;
|
|
7
|
+
// src/core/diagnostics/context.ts
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const os_1 = __importDefault(require("os"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const child_process_1 = require("child_process");
|
|
12
|
+
const providers_1 = require("../../providers");
|
|
13
|
+
const registries_1 = require("../registries");
|
|
14
|
+
const orchestrator_1 = require("../context/orchestrator");
|
|
15
|
+
const status_1 = require("../../commands/hooks/status");
|
|
16
|
+
const profile_1 = require("../profile");
|
|
17
|
+
const bundles_1 = require("../bundles");
|
|
18
|
+
const skill_integrity_1 = require("../skill-integrity");
|
|
19
|
+
function home() { return process.env.HOME || os_1.default.homedir(); }
|
|
20
|
+
function awmHome() { return process.env.AWM_HOME || path_1.default.join(home(), '.awm'); }
|
|
21
|
+
// Estado de un artefacto en <dir>/<skill>: link vivo / symlink colgante / ausente.
|
|
22
|
+
function linkState(dir, skill) {
|
|
23
|
+
const p = path_1.default.join(dir, skill);
|
|
24
|
+
let lst;
|
|
25
|
+
try {
|
|
26
|
+
lst = fs_1.default.lstatSync(p);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return 'absent';
|
|
30
|
+
}
|
|
31
|
+
if (lst.isSymbolicLink())
|
|
32
|
+
return fs_1.default.existsSync(p) ? 'present' : 'broken';
|
|
33
|
+
return 'present'; // un dir/archivo real también cuenta como presente
|
|
34
|
+
}
|
|
35
|
+
function classifyLinks(skillNames, dir) {
|
|
36
|
+
const linked = [];
|
|
37
|
+
const broken = [];
|
|
38
|
+
for (const s of skillNames) {
|
|
39
|
+
const st = linkState(dir, s);
|
|
40
|
+
if (st === 'present')
|
|
41
|
+
linked.push(s);
|
|
42
|
+
else if (st === 'broken')
|
|
43
|
+
broken.push(s);
|
|
44
|
+
}
|
|
45
|
+
return { linked, broken };
|
|
46
|
+
}
|
|
47
|
+
function detectGitState(repoDir) {
|
|
48
|
+
try {
|
|
49
|
+
const porcelain = (0, child_process_1.execSync)('git status --porcelain', { cwd: repoDir, stdio: ['ignore', 'pipe', 'ignore'] })
|
|
50
|
+
.toString().trim();
|
|
51
|
+
if (porcelain.length > 0)
|
|
52
|
+
return 'dirty';
|
|
53
|
+
try {
|
|
54
|
+
const behind = (0, child_process_1.execSync)('git rev-list --count HEAD..@{u}', { cwd: repoDir, stdio: ['ignore', 'pipe', 'ignore'], timeout: 3000 })
|
|
55
|
+
.toString().trim();
|
|
56
|
+
if (behind !== '' && behind !== '0')
|
|
57
|
+
return 'behind';
|
|
58
|
+
}
|
|
59
|
+
catch { /* sin upstream configurado */ }
|
|
60
|
+
return 'clean';
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return 'unknown';
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// Estado del contexto AWM para agentes cuyo mecanismo de inyección es config-instructions
|
|
67
|
+
// (hoy: opencode). claude-code usa el hook → ya se reporta como machine.hook, no se duplica.
|
|
68
|
+
// Sólo se reporta un agente si su archivo de config existe (señal de que el agente está en uso).
|
|
69
|
+
function gatherContextInjection() {
|
|
70
|
+
const out = [];
|
|
71
|
+
const orch = new orchestrator_1.InjectionOrchestrator();
|
|
72
|
+
for (const agent of Object.keys(providers_1.PROVIDERS)) {
|
|
73
|
+
const inj = providers_1.PROVIDERS[agent].injection;
|
|
74
|
+
if (!inj || inj.type !== 'config-instructions')
|
|
75
|
+
continue;
|
|
76
|
+
if (!fs_1.default.existsSync(inj.configPath))
|
|
77
|
+
continue;
|
|
78
|
+
let state = 'absent';
|
|
79
|
+
try {
|
|
80
|
+
state = orch.contextStatus({
|
|
81
|
+
agent,
|
|
82
|
+
scope: 'global',
|
|
83
|
+
registryRoot: (0, registries_1.capabilityRoot)('skills') ?? '',
|
|
84
|
+
installMethod: 'symlink',
|
|
85
|
+
profileExtensions: [],
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
// registry ausente u otra falla → tratar como ausente (no romper el doctor)
|
|
90
|
+
}
|
|
91
|
+
out.push({ agent, state });
|
|
92
|
+
}
|
|
93
|
+
return out;
|
|
94
|
+
}
|
|
95
|
+
function gatherMachine(bundles, agent = 'claude-code') {
|
|
96
|
+
// registryCache: estado del primer registry configurado (baseline en una máquina sembrada)
|
|
97
|
+
const regs = (0, registries_1.listRegistries)();
|
|
98
|
+
const first = regs[0];
|
|
99
|
+
const cachePresent = !!first && fs_1.default.existsSync(path_1.default.join(first.contentRoot, '.git'));
|
|
100
|
+
const gitState = cachePresent ? detectGitState(first.contentRoot) : undefined;
|
|
101
|
+
// hook (reutiliza computeHookStatus)
|
|
102
|
+
let hookPresent = false;
|
|
103
|
+
let hookDegraded = false;
|
|
104
|
+
try {
|
|
105
|
+
const hs = (0, status_1.computeHookStatus)('claude-code');
|
|
106
|
+
hookPresent = hs.checks.settingsEntry.ok;
|
|
107
|
+
hookDegraded = hs.overall === 'DEGRADED';
|
|
108
|
+
}
|
|
109
|
+
catch { /* sin soporte de hooks → ausente */ }
|
|
110
|
+
// devCore (bundle baseline)
|
|
111
|
+
const skillsDir = providers_1.PROVIDERS[agent].skill.global;
|
|
112
|
+
const baseline = bundles.find((b) => b.scope === 'baseline');
|
|
113
|
+
let devCorePresent = false;
|
|
114
|
+
let brokenLinks = [];
|
|
115
|
+
if (baseline) {
|
|
116
|
+
const skillNames = (0, bundles_1.resolveBundleSkills)(baseline.name, bundles);
|
|
117
|
+
const { linked, broken } = classifyLinks(skillNames, skillsDir);
|
|
118
|
+
const absent = skillNames.filter((s) => !linked.includes(s) && !broken.includes(s));
|
|
119
|
+
devCorePresent = skillNames.length > 0 && (linked.length + broken.length) > 0;
|
|
120
|
+
brokenLinks = [...broken, ...absent];
|
|
121
|
+
}
|
|
122
|
+
// ambient (deseados desde ~/.awm/config.json)
|
|
123
|
+
let wanted = [];
|
|
124
|
+
try {
|
|
125
|
+
const cfg = JSON.parse(fs_1.default.readFileSync(path_1.default.join(awmHome(), 'config.json'), 'utf-8'));
|
|
126
|
+
if (Array.isArray(cfg.ambient)) {
|
|
127
|
+
wanted = cfg.ambient.filter((x) => typeof x === 'string');
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
catch { /* sin config → ningún ambient deseado */ }
|
|
131
|
+
const installed = wanted.filter((name) => {
|
|
132
|
+
const skillNames = (0, bundles_1.resolveBundleSkills)(name, bundles);
|
|
133
|
+
if (skillNames.length === 0)
|
|
134
|
+
return false;
|
|
135
|
+
const { linked } = classifyLinks(skillNames, skillsDir);
|
|
136
|
+
return linked.length === skillNames.length;
|
|
137
|
+
});
|
|
138
|
+
return {
|
|
139
|
+
registryCache: { present: cachePresent, gitState },
|
|
140
|
+
hook: { present: hookPresent, degraded: hookDegraded },
|
|
141
|
+
devCore: { present: devCorePresent, brokenLinks },
|
|
142
|
+
ambient: { wanted, installed },
|
|
143
|
+
contextInjection: gatherContextInjection(),
|
|
144
|
+
globalSkills: (0, skill_integrity_1.classifyGlobalSkills)(skillsDir, (0, registries_1.contentRoots)()),
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
function gatherProject(root, bundles, agent = 'claude-code') {
|
|
148
|
+
const profile = (0, profile_1.readProfile)(root);
|
|
149
|
+
const profilePresent = fs_1.default.existsSync(path_1.default.join(root, '.awm', 'profile.json'));
|
|
150
|
+
const localSkillsDir = path_1.default.join(root, providers_1.PROVIDERS[agent].skill.local);
|
|
151
|
+
const expected = [];
|
|
152
|
+
for (const ext of profile.extensions) {
|
|
153
|
+
for (const s of (0, bundles_1.resolveBundleSkills)(ext, bundles))
|
|
154
|
+
if (!expected.includes(s))
|
|
155
|
+
expected.push(s);
|
|
156
|
+
}
|
|
157
|
+
const { linked, broken } = classifyLinks(expected, localSkillsDir);
|
|
158
|
+
let context = { present: false };
|
|
159
|
+
if (fs_1.default.existsSync(path_1.default.join(root, 'CLAUDE.md')))
|
|
160
|
+
context = { present: true, file: 'CLAUDE.md' };
|
|
161
|
+
else if (fs_1.default.existsSync(path_1.default.join(root, 'AGENTS.md')))
|
|
162
|
+
context = { present: true, file: 'AGENTS.md' };
|
|
163
|
+
return {
|
|
164
|
+
root,
|
|
165
|
+
profile: { present: profilePresent, extensions: profile.extensions },
|
|
166
|
+
activeBundles: { expected, linked, broken },
|
|
167
|
+
sensors: { present: fs_1.default.existsSync(path_1.default.join(root, '.awm', 'sensors.json')) },
|
|
168
|
+
constitution: { present: fs_1.default.existsSync(path_1.default.join(root, 'CONSTITUTION.md')) },
|
|
169
|
+
context,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
function gatherContext(opts = {}) {
|
|
173
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
174
|
+
const bundles = opts.bundles ?? (0, bundles_1.discoverAllBundles)();
|
|
175
|
+
const agent = opts.agent ?? 'claude-code';
|
|
176
|
+
const root = (0, profile_1.findProjectRoot)(cwd);
|
|
177
|
+
return {
|
|
178
|
+
machine: gatherMachine(bundles, agent),
|
|
179
|
+
project: root ? gatherProject(root, bundles, agent) : null,
|
|
180
|
+
};
|
|
181
|
+
}
|