@cristiancorreau/forge 3.0.1 → 3.2.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 (96) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +265 -109
  3. package/assets/adapters/claude-code/commands/laravel-eloquent.md +7 -0
  4. package/assets/adapters/claude-code/commands/laravel-mcp.md +7 -0
  5. package/assets/adapters/claude-code/commands/laravel-pest.md +7 -0
  6. package/assets/adapters/claude-code/commands/laravel-security.md +7 -0
  7. package/assets/adapters/claude-code/commands/laravel-verify.md +7 -0
  8. package/assets/core/hooks/pre-bash-check.js +46 -0
  9. package/assets/core/hooks/pre-edit-check.js +24 -1
  10. package/assets/core/schemas/project.schema.json +3 -1
  11. package/assets/core/skills/laravel-eloquent/SKILL.md +453 -0
  12. package/assets/core/skills/laravel-mcp/SKILL.md +468 -0
  13. package/assets/core/skills/laravel-pest/SKILL.md +686 -0
  14. package/assets/core/skills/laravel-security/SKILL.md +658 -0
  15. package/assets/core/skills/laravel-verify/SKILL.md +462 -0
  16. package/assets/manifest.json +27 -2
  17. package/assets/profiles/astro/agents/frontend-engineer.md +2 -0
  18. package/assets/profiles/django/agents/api-engineer.md +2 -0
  19. package/assets/profiles/expo/agents/mobile-engineer.md +2 -0
  20. package/assets/profiles/express/agents/api-engineer.md +2 -0
  21. package/assets/profiles/fastapi/agents/api-engineer.md +2 -0
  22. package/assets/profiles/flask/agents/api-engineer.md +2 -0
  23. package/assets/profiles/flutter/agents/mobile-engineer.md +12 -10
  24. package/assets/profiles/go-gin/agents/api-engineer.md +3 -1
  25. package/assets/profiles/hono-drizzle/agents/api-engineer.md +2 -0
  26. package/assets/profiles/laravel/README.md +16 -2
  27. package/assets/profiles/laravel/agents/api-engineer.md +2 -0
  28. package/assets/profiles/laravel/agents/fullstack-engineer.md +4 -2
  29. package/assets/profiles/laravel/agents/laravel-specialist.md +607 -0
  30. package/assets/profiles/laravel/agents/laravel-test-engineer.md +448 -0
  31. package/assets/profiles/nestjs/agents/api-engineer.md +3 -1
  32. package/assets/profiles/nextjs-admin/agents/admin-engineer.md +2 -0
  33. package/assets/profiles/playwright-crawler/agents/scanner-engineer.md +2 -0
  34. package/assets/profiles/rails/agents/fullstack-engineer.md +2 -0
  35. package/assets/profiles/rust/agents/api-engineer.md +2 -0
  36. package/assets/profiles/springboot/agents/api-engineer.md +11 -9
  37. package/assets/profiles/sveltekit/agents/frontend-engineer.md +4 -2
  38. package/assets/profiles/vuenuxt/agents/frontend-engineer.md +12 -10
  39. package/assets/profiles/wordpress/agents/divi-engineer.md +2 -0
  40. package/assets/profiles/wordpress/agents/elementor-engineer.md +2 -0
  41. package/dist/cli.js +15 -0
  42. package/dist/cli.js.map +1 -1
  43. package/dist/commands/add.d.ts +2 -0
  44. package/dist/commands/add.d.ts.map +1 -0
  45. package/dist/commands/add.js +187 -0
  46. package/dist/commands/add.js.map +1 -0
  47. package/dist/commands/mcp.d.ts +42 -0
  48. package/dist/commands/mcp.d.ts.map +1 -0
  49. package/dist/commands/mcp.js +141 -0
  50. package/dist/commands/mcp.js.map +1 -0
  51. package/dist/commands/update.d.ts +30 -0
  52. package/dist/commands/update.d.ts.map +1 -0
  53. package/dist/commands/update.js +180 -0
  54. package/dist/commands/update.js.map +1 -0
  55. package/dist/commands/validate.d.ts.map +1 -1
  56. package/dist/commands/validate.js +40 -1
  57. package/dist/commands/validate.js.map +1 -1
  58. package/dist/lib/catalog.d.ts +7 -0
  59. package/dist/lib/catalog.d.ts.map +1 -1
  60. package/dist/lib/catalog.js +20 -0
  61. package/dist/lib/catalog.js.map +1 -1
  62. package/dist/lib/mcp-tools.d.ts +37 -0
  63. package/dist/lib/mcp-tools.d.ts.map +1 -0
  64. package/dist/lib/mcp-tools.js +124 -0
  65. package/dist/lib/mcp-tools.js.map +1 -0
  66. package/dist/lib/skill-security.d.ts +66 -0
  67. package/dist/lib/skill-security.d.ts.map +1 -0
  68. package/dist/lib/skill-security.js +225 -0
  69. package/dist/lib/skill-security.js.map +1 -0
  70. package/dist/lib/skill-source.d.ts +29 -0
  71. package/dist/lib/skill-source.d.ts.map +1 -0
  72. package/dist/lib/skill-source.js +94 -0
  73. package/dist/lib/skill-source.js.map +1 -0
  74. package/dist/tui/dashboard.d.ts.map +1 -1
  75. package/dist/tui/dashboard.js +3 -6
  76. package/dist/tui/dashboard.js.map +1 -1
  77. package/dist/tui/panel.d.ts.map +1 -1
  78. package/dist/tui/panel.js +3 -6
  79. package/dist/tui/panel.js.map +1 -1
  80. package/dist/tui/wizard.d.ts.map +1 -1
  81. package/dist/tui/wizard.js +3 -13
  82. package/dist/tui/wizard.js.map +1 -1
  83. package/dist/ui/colors.d.ts +3 -1
  84. package/dist/ui/colors.d.ts.map +1 -1
  85. package/dist/ui/colors.js +11 -2
  86. package/dist/ui/colors.js.map +1 -1
  87. package/dist/ui/header.d.ts.map +1 -1
  88. package/dist/ui/header.js +4 -3
  89. package/dist/ui/header.js.map +1 -1
  90. package/dist/ui/theme.d.ts +24 -0
  91. package/dist/ui/theme.d.ts.map +1 -0
  92. package/dist/ui/theme.js +32 -0
  93. package/dist/ui/theme.js.map +1 -0
  94. package/dist/version.d.ts +1 -1
  95. package/dist/version.js +1 -1
  96. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -1,8 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import { init } from './commands/init.js';
3
3
  import { adopt } from './commands/adopt.js';
4
+ import { add } from './commands/add.js';
4
5
  import { audit } from './commands/audit.js';
5
6
  import { generate } from './commands/generate.js';
7
+ import { update } from './commands/update.js';
6
8
  import { validate } from './commands/validate.js';
7
9
  import { doctor } from './commands/doctor.js';
8
10
  import { migrate } from './commands/migrate.js';
@@ -13,6 +15,7 @@ import { scaffold } from './commands/scaffold.js';
13
15
  import { teardown } from './commands/teardown.js';
14
16
  import { sessionStart, sessionClose } from './commands/session.js';
15
17
  import { panel } from './commands/panel.js';
18
+ import { mcp } from './commands/mcp.js';
16
19
  import { findProjectYaml } from './lib/yaml.js';
17
20
  import { VERSION } from './version.js';
18
21
  const HELP = `forge v${VERSION} — Agentic development framework
@@ -23,7 +26,9 @@ Setup
23
26
  panel Open the interactive panel (config, monitor, skills, hooks, templates)
24
27
  init Initialize forge in a project (wizard + post-install dashboard)
25
28
  adopt Onboard forge into an EXISTING codebase (analyze + auto-wiki)
29
+ add Install a skill from an external source (security pipeline, opt-in network)
26
30
  generate Generate runtime config files from project.yaml
31
+ update Update managed files to the bundled catalog (--dry-run, --force)
27
32
  migrate Migrate project.yaml from the v1 schema to v2 (--dry-run, --backup)
28
33
  scaffold Scaffold a new agent: Tier 2 profile, or Tier 3 domain agent (--tier 3)
29
34
  teardown Cleanly uninstall forge from a project (manifest-driven)
@@ -41,6 +46,7 @@ Workflow
41
46
 
42
47
  Knowledge
43
48
  wiki Manage the project knowledge base (status | ingest | query | lint)
49
+ mcp Run forge's MCP server (stdio, opt-in): live guardrail_status + wiki_search
44
50
 
45
51
  Options:
46
52
  -v, --version Show version
@@ -66,12 +72,18 @@ switch (cmd) {
66
72
  case 'adopt':
67
73
  exitCode = await adopt(rest);
68
74
  break;
75
+ case 'add':
76
+ exitCode = await add(rest);
77
+ break;
69
78
  case 'audit':
70
79
  exitCode = await audit(rest);
71
80
  break;
72
81
  case 'generate':
73
82
  exitCode = await generate(rest);
74
83
  break;
84
+ case 'update':
85
+ exitCode = await update(rest);
86
+ break;
75
87
  case 'validate':
76
88
  exitCode = await validate(rest);
77
89
  break;
@@ -105,6 +117,9 @@ switch (cmd) {
105
117
  case 'panel':
106
118
  exitCode = await panel(rest);
107
119
  break;
120
+ case 'mcp':
121
+ exitCode = await mcp(rest);
122
+ break;
108
123
  case '-v':
109
124
  case '--version':
110
125
  console.log(VERSION);
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function add(args: string[]): Promise<number>;
2
+ //# sourceMappingURL=add.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add.d.ts","sourceRoot":"","sources":["../../src/commands/add.ts"],"names":[],"mappings":"AAuHA,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAiFzD"}
@@ -0,0 +1,187 @@
1
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
2
+ import { join, dirname } from 'path';
3
+ import { createHash } from 'crypto';
4
+ import { resolveSource, fetchSkill } from '../lib/skill-source.js';
5
+ import { assessSkill, demoteRiskyLines, } from '../lib/skill-security.js';
6
+ const HELP = `Usage: forge add <source> [options]
7
+
8
+ Install a skill from an external source, behind a security pipeline.
9
+
10
+ Source:
11
+ owner/repo[/subpath][@ref] GitHub (the only command that uses the network)
12
+ ./path or /path Local SKILL.md or directory (offline)
13
+
14
+ Options:
15
+ --dry-run Run the full security analysis, write nothing
16
+ --yes Proceed without an interactive prompt (still blocks high risk)
17
+ --force Override a BLOCK verdict (high-severity findings) — destructive
18
+ --path <p> Subpath within the repo where SKILL.md lives
19
+ -h, --help Show this help
20
+
21
+ Security: forge analyses the content (invisible-char hygiene, risk scan, declared
22
+ capabilities), pins the ref to an immutable sha, records provenance in
23
+ .forge/externals.json, and relies on the runtime guardrail hooks as backstop. It
24
+ never auto-deletes content; high-severity findings block the install.
25
+ `;
26
+ const SEV_ICON = { high: '✗ ALTA ', medium: '⚠ MEDIA ', low: '· BAJA ' };
27
+ function findProjectRoot(start) {
28
+ let dir = start;
29
+ while (true) {
30
+ if (existsSync(join(dir, '.forge', 'manifest.json')) ||
31
+ existsSync(join(dir, 'project.yaml')) ||
32
+ existsSync(join(dir, '.git')))
33
+ return dir;
34
+ const parent = dirname(dir);
35
+ if (parent === dir)
36
+ return start;
37
+ dir = parent;
38
+ }
39
+ }
40
+ function printReport(a, label, sha) {
41
+ process.stdout.write(`\nforge add — analisis de seguridad\n\n`);
42
+ process.stdout.write(` Fuente: ${label}\n`);
43
+ process.stdout.write(` Pin: ${sha}\n`);
44
+ process.stdout.write(` Skill: ${a.name ?? '(sin nombre)'}\n`);
45
+ if (!a.formatOk) {
46
+ process.stdout.write(` Formato: INVALIDO — ${a.formatIssues.join('; ')}\n`);
47
+ }
48
+ const all = [...a.hygiene.issues, ...a.findings]
49
+ .sort((x, y) => (x.severity === y.severity ? x.line - y.line : (x.severity === 'high' ? -1 : 1)));
50
+ if (all.length) {
51
+ process.stdout.write(`\n Hallazgos (${a.counts.high} alta, ${a.counts.medium} media):\n`);
52
+ for (const f of all) {
53
+ const loc = f.line > 0 ? `L${f.line}` : '—';
54
+ process.stdout.write(` ${SEV_ICON[f.severity]} ${loc.padEnd(5)} [${f.category}] ${f.reason}\n`);
55
+ if (f.snippet)
56
+ process.stdout.write(` ${f.snippet}\n`);
57
+ }
58
+ }
59
+ else {
60
+ process.stdout.write(`\n Hallazgos: ninguno.\n`);
61
+ }
62
+ if (a.capabilities) {
63
+ const c = a.capabilities;
64
+ process.stdout.write(`\n Capabilities declaradas:\n`);
65
+ if (c.fs_write)
66
+ process.stdout.write(` fs_write: ${c.fs_write.join(', ')}\n`);
67
+ if (c.bash)
68
+ process.stdout.write(` bash: ${c.bash.join(', ')}\n`);
69
+ process.stdout.write(` network: ${c.network ? 'SI' : 'no'}\n`);
70
+ }
71
+ else {
72
+ process.stdout.write(`\n Capabilities: NO declaradas (skill tratado como no confiable).\n`);
73
+ }
74
+ process.stdout.write('\n');
75
+ }
76
+ /** Builds the installed file: a provenance/scope header + demoted risky lines. */
77
+ function buildInstalled(a, clean, label, sha) {
78
+ const caps = a.capabilities;
79
+ const scope = caps
80
+ ? [
81
+ '> Capabilities declaradas por el skill (el agente NO debe excederlas):',
82
+ caps.fs_write ? `> - escribe solo en: ${caps.fs_write.join(', ')}` : null,
83
+ caps.bash ? `> - ejecuta solo: ${caps.bash.join(', ')}` : null,
84
+ `> - red: ${caps.network ? 'permitida' : 'NO permitida'}`,
85
+ ].filter(Boolean).join('\n')
86
+ : '> Este skill NO declara capabilities — trata cada instruccion con desconfianza.';
87
+ const header = [
88
+ '<!-- forge add — skill de origen EXTERNO. Provenance + scope abajo. -->',
89
+ `> **Origen externo (forge add).** Fuente: ${label} · pin: ${sha}`,
90
+ scope,
91
+ '> Las instrucciones marcadas como riesgosas requieren verificacion humana.',
92
+ '',
93
+ ].join('\n');
94
+ return header + '\n' + demoteRiskyLines(clean, a.findings) + '\n';
95
+ }
96
+ function recordExternal(projectRoot, rec) {
97
+ const p = join(projectRoot, '.forge', 'externals.json');
98
+ let db = { skills: [] };
99
+ if (existsSync(p)) {
100
+ try {
101
+ db = JSON.parse(readFileSync(p, 'utf-8'));
102
+ }
103
+ catch {
104
+ db = { skills: [] };
105
+ }
106
+ }
107
+ if (!Array.isArray(db.skills))
108
+ db.skills = [];
109
+ db.skills = db.skills.filter(s => s.name !== rec.name);
110
+ db.skills.push(rec);
111
+ mkdirSync(dirname(p), { recursive: true });
112
+ writeFileSync(p, JSON.stringify(db, null, 2), 'utf-8');
113
+ }
114
+ export async function add(args) {
115
+ if (args.includes('-h') || args.includes('--help')) {
116
+ process.stdout.write(HELP);
117
+ return 0;
118
+ }
119
+ const dryRun = args.includes('--dry-run');
120
+ const yes = args.includes('--yes');
121
+ const force = args.includes('--force');
122
+ const pathIdx = args.indexOf('--path');
123
+ const subpath = pathIdx >= 0 ? args[pathIdx + 1] : undefined;
124
+ const spec = args.find((a, i) => !a.startsWith('-') && args[i - 1] !== '--path');
125
+ if (!spec) {
126
+ process.stderr.write('forge add: falta la fuente. Ver `forge add --help`.\n');
127
+ return 1;
128
+ }
129
+ // Resolve + fetch (the only network step, only for github sources).
130
+ let source, fetched;
131
+ try {
132
+ source = resolveSource(spec, { path: subpath });
133
+ fetched = await fetchSkill(source);
134
+ }
135
+ catch (e) {
136
+ process.stderr.write(`forge add: no se pudo obtener el skill — ${e.message}\n`);
137
+ return 1;
138
+ }
139
+ const a = assessSkill(fetched.content);
140
+ printReport(a, fetched.label, fetched.sha);
141
+ // Policy gate.
142
+ if (!a.formatOk) {
143
+ process.stderr.write('BLOQUEADO: el contenido no es un SKILL.md valido. No se instalo nada.\n');
144
+ return 1;
145
+ }
146
+ if (a.decision === 'block' && !force) {
147
+ process.stderr.write(`BLOQUEADO: ${a.counts.high} hallazgo(s) de severidad alta. Revisa la fuente.\n` +
148
+ `Si confias en ella, podes forzar con --force (destructivo). No se instalo nada.\n`);
149
+ return 1;
150
+ }
151
+ if (a.decision !== 'allow' && !yes && !force) {
152
+ process.stdout.write('Requiere confirmacion: volve a correr con --yes para instalar (o --force si hay severidad alta).\n' +
153
+ 'No se instalo nada.\n');
154
+ return dryRun ? 0 : 1;
155
+ }
156
+ if (a.decision === 'block' && force) {
157
+ process.stdout.write('⚠ --force: instalando pese a hallazgos de severidad alta. Bajo tu responsabilidad.\n');
158
+ }
159
+ if (dryRun) {
160
+ process.stdout.write('--dry-run: analisis completo, no se escribio nada.\n');
161
+ return 0;
162
+ }
163
+ // Install: provenance header + demoted risky lines, into .claude/skills/<name>/.
164
+ const projectRoot = findProjectRoot(process.cwd());
165
+ const installed = buildInstalled(a, a.hygiene.clean, fetched.label, fetched.sha);
166
+ const rel = join('.claude', 'skills', a.name, 'SKILL.md');
167
+ const dest = join(projectRoot, rel);
168
+ mkdirSync(dirname(dest), { recursive: true });
169
+ writeFileSync(dest, installed, 'utf-8');
170
+ recordExternal(projectRoot, {
171
+ name: a.name,
172
+ source: source.kind === 'github' ? `${source.owner}/${source.repo}` : 'local',
173
+ ref: source.ref ?? (source.kind === 'local' ? 'local' : 'HEAD'),
174
+ sha: fetched.sha,
175
+ contentSha256: createHash('sha256').update(a.hygiene.clean, 'utf-8').digest('hex'),
176
+ capabilities: a.capabilities,
177
+ risk: { high: a.counts.high, medium: a.counts.medium },
178
+ path: rel,
179
+ installedAt: new Date().toISOString(),
180
+ });
181
+ process.stdout.write(`Instalado: ${rel} (registrado en .forge/externals.json).\n`);
182
+ if (!a.capabilities) {
183
+ process.stdout.write('Recordatorio: skill sin capabilities declaradas — revisalo antes de activarlo.\n');
184
+ }
185
+ return 0;
186
+ }
187
+ //# sourceMappingURL=add.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add.js","sourceRoot":"","sources":["../../src/commands/add.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ /** The advertised tools. MUST stay a subset of MCP_TOOLS (golden-rule allowlist). */
2
+ export declare const TOOL_DEFS: ({
3
+ name: string;
4
+ description: string;
5
+ inputSchema: {
6
+ type: string;
7
+ properties: {
8
+ command: {
9
+ type: string;
10
+ description: string;
11
+ };
12
+ file: {
13
+ type: string;
14
+ description: string;
15
+ };
16
+ content: {
17
+ type: string;
18
+ description: string;
19
+ };
20
+ query?: undefined;
21
+ };
22
+ required?: undefined;
23
+ };
24
+ } | {
25
+ name: string;
26
+ description: string;
27
+ inputSchema: {
28
+ type: string;
29
+ properties: {
30
+ query: {
31
+ type: string;
32
+ description: string;
33
+ };
34
+ command?: undefined;
35
+ file?: undefined;
36
+ content?: undefined;
37
+ };
38
+ required: string[];
39
+ };
40
+ })[];
41
+ export declare function mcp(args: string[]): Promise<number>;
42
+ //# sourceMappingURL=mcp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../../src/commands/mcp.ts"],"names":[],"mappings":"AAqEA,qFAAqF;AACrF,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAwBrB,CAAC;AAEF,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAsDzD"}
@@ -0,0 +1,141 @@
1
+ import { existsSync } from 'fs';
2
+ import { join, dirname } from 'path';
3
+ import { createRequire } from 'module';
4
+ import { fileURLToPath } from 'url';
5
+ import { VERSION } from '../version.js';
6
+ import { guardrailStatus, wikiSearch, MCP_TOOLS, } from '../lib/mcp-tools.js';
7
+ const HELP = `Usage: forge mcp [options]
8
+
9
+ Run forge's MCP server (stdio) so an MCP-aware runtime can query the project's
10
+ LIVE state. Opt-in and additive: everything forge knows is still in the static
11
+ files; MCP only adds two dynamic, read-only tools.
12
+
13
+ Tools:
14
+ guardrail_status Live verdict of the project's guardrail hooks for a command
15
+ or a file edit (would it be blocked/warned?).
16
+ wiki_search Search the project's wiki/ knowledge pages (confined corpus).
17
+
18
+ Transport: stdio only (no network, no HTTP). Register it once with your runtime:
19
+ claude mcp add -s local -t stdio forge -- forge mcp
20
+
21
+ Requires @modelcontextprotocol/sdk (lazy, NOT a forge dependency — install it in
22
+ your project: npm i @modelcontextprotocol/sdk). This keeps the cold-start of
23
+ forge's other commands untouched for everyone who does not use MCP.
24
+
25
+ Options:
26
+ -h, --help Show this help
27
+ `;
28
+ function findProjectRoot(start) {
29
+ let dir = start;
30
+ while (true) {
31
+ if (existsSync(join(dir, '.forge', 'manifest.json')) ||
32
+ existsSync(join(dir, 'project.yaml')) ||
33
+ existsSync(join(dir, '.git')))
34
+ return dir;
35
+ const parent = dirname(dir);
36
+ if (parent === dir)
37
+ return start;
38
+ dir = parent;
39
+ }
40
+ }
41
+ /**
42
+ * Lazily resolves @modelcontextprotocol/sdk, trying the consuming project's
43
+ * node_modules first, then forge's own. Returns null if not resolvable (the
44
+ * caller prints an actionable message). Typed `any` on purpose: the SDK is not a
45
+ * forge dependency, so tsc must not try to resolve it at build time.
46
+ */
47
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
48
+ function loadSdk() {
49
+ const makers = [
50
+ () => createRequire(join(process.cwd(), 'index.js')),
51
+ () => createRequire(fileURLToPath(import.meta.url)),
52
+ ];
53
+ for (const make of makers) {
54
+ try {
55
+ const req = make();
56
+ return {
57
+ Server: req('@modelcontextprotocol/sdk/server/index.js').Server,
58
+ StdioServerTransport: req('@modelcontextprotocol/sdk/server/stdio.js').StdioServerTransport,
59
+ types: req('@modelcontextprotocol/sdk/types.js'),
60
+ };
61
+ }
62
+ catch { /* try the next resolver */ }
63
+ }
64
+ return null;
65
+ }
66
+ /** The advertised tools. MUST stay a subset of MCP_TOOLS (golden-rule allowlist). */
67
+ export const TOOL_DEFS = [
68
+ {
69
+ name: 'guardrail_status',
70
+ description: "Live verdict of the project's guardrail hooks. Pass `command` (shell) or " +
71
+ '`file` (+ optional `content`). Read-only: it never runs the command.',
72
+ inputSchema: {
73
+ type: 'object',
74
+ properties: {
75
+ command: { type: 'string', description: 'A shell command to evaluate.' },
76
+ file: { type: 'string', description: 'A file path to evaluate for editing.' },
77
+ content: { type: 'string', description: 'Optional new content for the file edit.' },
78
+ },
79
+ },
80
+ },
81
+ {
82
+ name: 'wiki_search',
83
+ description: "Search the project's wiki/ knowledge pages (lexical, confined to wiki/).",
84
+ inputSchema: {
85
+ type: 'object',
86
+ properties: { query: { type: 'string', description: 'Text to search for.' } },
87
+ required: ['query'],
88
+ },
89
+ },
90
+ ];
91
+ export async function mcp(args) {
92
+ if (args.includes('-h') || args.includes('--help')) {
93
+ process.stdout.write(HELP);
94
+ return 0;
95
+ }
96
+ const sdk = loadSdk();
97
+ if (!sdk) {
98
+ process.stderr.write('forge mcp: falta @modelcontextprotocol/sdk.\n' +
99
+ ' Es una dependencia opt-in (no se instala con forge para no pesar en el cold-start).\n' +
100
+ ' Instalala en tu proyecto: npm i @modelcontextprotocol/sdk\n');
101
+ return 1;
102
+ }
103
+ const projectRoot = findProjectRoot(process.cwd());
104
+ const { Server, StdioServerTransport, types } = sdk;
105
+ const server = new Server({ name: 'forge', version: VERSION }, { capabilities: { tools: {} } });
106
+ server.setRequestHandler(types.ListToolsRequestSchema, async () => ({ tools: TOOL_DEFS }));
107
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
108
+ server.setRequestHandler(types.CallToolRequestSchema, async (req) => {
109
+ const name = req.params?.name;
110
+ const a = (req.params?.arguments ?? {});
111
+ // Hard allowlist: only the two declared tools are callable.
112
+ if (!MCP_TOOLS.includes(name)) {
113
+ return { isError: true, content: [{ type: 'text', text: `tool desconocida: ${name}` }] };
114
+ }
115
+ if (name === 'guardrail_status') {
116
+ const input = {
117
+ command: typeof a.command === 'string' ? a.command : undefined,
118
+ file: typeof a.file === 'string' ? a.file : undefined,
119
+ content: typeof a.content === 'string' ? a.content : undefined,
120
+ };
121
+ const r = guardrailStatus(input, projectRoot);
122
+ return { content: [{ type: 'text', text: JSON.stringify(r, null, 2) }] };
123
+ }
124
+ // wiki_search
125
+ const hits = wikiSearch(typeof a.query === 'string' ? a.query : '', projectRoot);
126
+ const text = hits.length
127
+ ? hits.map(h => `${h.page}:${h.line} ${h.snippet}`).join('\n')
128
+ : 'sin coincidencias en wiki/';
129
+ return { content: [{ type: 'text', text }] };
130
+ });
131
+ const transport = new StdioServerTransport();
132
+ await server.connect(transport);
133
+ process.stderr.write('forge mcp: servidor MCP (stdio) activo. Ctrl-C para salir.\n');
134
+ // Keep the process alive until the client disconnects or we are interrupted.
135
+ return await new Promise((resolve) => {
136
+ transport.onclose = () => resolve(0);
137
+ process.on('SIGINT', () => resolve(0));
138
+ process.on('SIGTERM', () => resolve(0));
139
+ });
140
+ }
141
+ //# sourceMappingURL=mcp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.js","sourceRoot":"","sources":["../../src/commands/mcp.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import { type ForgeManifest } from '../lib/lock.js';
2
+ /**
3
+ * Maps a managed file's project-relative path (as stored in the manifest) to its
4
+ * source path inside the forge catalog (forgeRoot). Returns null when the file is
5
+ * generated or has no 1:1 catalog source (e.g. a Tier 3 project-specific agent
6
+ * that doesn't live in core/agents/ or any profile's agents/ dir).
7
+ *
8
+ * Mapping:
9
+ * .claude/hooks/<f> -> core/hooks/<f>
10
+ * .claude/commands/<...> -> adapters/claude-code/commands/<...>
11
+ * .claude/agents/<f>.md -> core/agents/<f>.md OR a profile's agents/ dir
12
+ */
13
+ export declare function resolveCatalogSource(relPath: string, forgeRoot: string): string | null;
14
+ interface UpdatePlanEntry {
15
+ /** project-relative path */
16
+ file: string;
17
+ /** absolute catalog source path */
18
+ source: string;
19
+ /** 'update' (unmodified, catalog differs), 'modified' (user edited), 'missing' (deleted on disk) */
20
+ kind: 'update' | 'modified' | 'missing';
21
+ }
22
+ /**
23
+ * Computes the update plan from a manifest. For each managed file with a catalog
24
+ * source it decides whether the file would be updated, is user-modified, or is
25
+ * missing on disk. Files in sync (disk == manifest == catalog) are omitted.
26
+ */
27
+ export declare function computeUpdatePlan(manifest: ForgeManifest, projRoot: string, forgeRoot: string): UpdatePlanEntry[];
28
+ export declare function update(args: string[]): Promise<number>;
29
+ export {};
30
+ //# sourceMappingURL=update.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../src/commands/update.ts"],"names":[],"mappings":"AAIA,OAAO,EAA0C,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAkC5F;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CA+BtF;AAED,UAAU,eAAe;IACvB,4BAA4B;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,mCAAmC;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,oGAAoG;IACpG,IAAI,EAAE,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC;CACzC;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,aAAa,EACvB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAChB,eAAe,EAAE,CA2BnB;AAED,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAiF5D"}