@open-mercato/cli 0.7.1-develop.7193.1.910a5b0a1e → 0.7.1-develop.7194.1.ab4fc81f82
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/.turbo/turbo-build.log +6 -2
- package/dist/agentic/github-copilot/copilot-instructions.md.template +37 -0
- package/dist/agentic/github-copilot/instructions/entity-guard.instructions.md +27 -0
- package/dist/agentic/github-copilot/instructions/generated-guard.instructions.md +10 -0
- package/dist/agentic/github-copilot/mcp.json.example +13 -0
- package/dist/agentic/guides/module-facts.json +11527 -283
- package/dist/agentic/guides/module-facts.v2.json +11527 -283
- package/dist/agentic/guides/modules/agent_orchestrator/acl-features.md +31 -0
- package/dist/agentic/guides/modules/agent_orchestrator/active-extension-bindings.md +12 -0
- package/dist/agentic/guides/modules/agent_orchestrator/ai-agents.md +12 -0
- package/dist/agentic/guides/modules/agent_orchestrator/ai-tools-mcp-capabilities.md +13 -0
- package/dist/agentic/guides/modules/agent_orchestrator/backend-pages.md +28 -0
- package/dist/agentic/guides/modules/agent_orchestrator/cli-commands.md +14 -0
- package/dist/agentic/guides/modules/agent_orchestrator/contribution-resolutions.md +49 -0
- package/dist/agentic/guides/modules/agent_orchestrator/di-registrations-rich.md +42 -0
- package/dist/agentic/guides/modules/agent_orchestrator/di-service-tokens.md +24 -0
- package/dist/agentic/guides/modules/agent_orchestrator/domain-commands.md +28 -0
- package/dist/agentic/guides/modules/agent_orchestrator/encryption.md +20 -0
- package/dist/agentic/guides/modules/agent_orchestrator/entities.md +31 -0
- package/dist/agentic/guides/modules/agent_orchestrator/events.md +38 -0
- package/dist/agentic/guides/modules/agent_orchestrator/exact-override-targets.md +122 -0
- package/dist/agentic/guides/modules/agent_orchestrator/host-extension-points.md +10 -0
- package/dist/agentic/guides/modules/agent_orchestrator/index.md +31 -0
- package/dist/agentic/guides/modules/agent_orchestrator/owned-contract-module-metadata.md +11 -0
- package/dist/agentic/guides/modules/agent_orchestrator/setup.md +11 -0
- package/dist/agentic/guides/modules/agent_orchestrator/umes-contributions.md +49 -0
- package/dist/agentic/guides/modules/agent_orchestrator/umes-hosts.md +61 -0
- package/dist/agentic/guides/modules/agent_orchestrator/workers.md +14 -0
- package/dist/agentic/guides/modules/ai_assistant/di-registrations-rich.md +3 -2
- package/dist/agentic/guides/modules/ai_assistant/di-service-tokens.md +2 -1
- package/dist/agentic/guides/modules/ai_assistant/exact-override-targets.md +3 -2
- package/dist/agentic/guides/modules/ai_assistant/index.md +1 -1
- package/dist/agentic/guides/modules/api_docs/index.md +1 -1
- package/dist/agentic/guides/modules/api_keys/index.md +1 -1
- package/dist/agentic/guides/modules/attachments/index.md +1 -1
- package/dist/agentic/guides/modules/audit_logs/index.md +1 -1
- package/dist/agentic/guides/modules/auth/index.md +1 -1
- package/dist/agentic/guides/modules/business_rules/index.md +1 -1
- package/dist/agentic/guides/modules/catalog/index.md +1 -1
- package/dist/agentic/guides/modules/channel_apns/index.md +1 -1
- package/dist/agentic/guides/modules/channel_discord/index.md +1 -1
- package/dist/agentic/guides/modules/channel_expo/index.md +1 -1
- package/dist/agentic/guides/modules/channel_fcm/index.md +1 -1
- package/dist/agentic/guides/modules/channel_gmail/index.md +1 -1
- package/dist/agentic/guides/modules/channel_imap/index.md +1 -1
- package/dist/agentic/guides/modules/channel_resend/index.md +1 -1
- package/dist/agentic/guides/modules/channel_ses/index.md +1 -1
- package/dist/agentic/guides/modules/checkout/index.md +1 -1
- package/dist/agentic/guides/modules/communication_channels/index.md +1 -1
- package/dist/agentic/guides/modules/configs/index.md +1 -1
- package/dist/agentic/guides/modules/content/index.md +1 -1
- package/dist/agentic/guides/modules/currencies/index.md +1 -1
- package/dist/agentic/guides/modules/customer_accounts/cli-commands.md +11 -0
- package/dist/agentic/guides/modules/customer_accounts/exact-override-targets.md +5 -4
- package/dist/agentic/guides/modules/customer_accounts/index.md +3 -2
- package/dist/agentic/guides/modules/customer_accounts/setup.md +1 -1
- package/dist/agentic/guides/modules/customers/contribution-resolutions.md +1 -0
- package/dist/agentic/guides/modules/customers/exact-override-targets.md +1 -0
- package/dist/agentic/guides/modules/customers/incoming-installed-contributions.md +3 -0
- package/dist/agentic/guides/modules/customers/index.md +2 -2
- package/dist/agentic/guides/modules/customers/umes-contributions.md +1 -0
- package/dist/agentic/guides/modules/dashboards/index.md +1 -1
- package/dist/agentic/guides/modules/data_sync/index.md +1 -1
- package/dist/agentic/guides/modules/design_system/index.md +1 -1
- package/dist/agentic/guides/modules/devices/index.md +1 -1
- package/dist/agentic/guides/modules/dictionaries/index.md +1 -1
- package/dist/agentic/guides/modules/directory/index.md +1 -1
- package/dist/agentic/guides/modules/documents/index.md +1 -1
- package/dist/agentic/guides/modules/entities/index.md +1 -1
- package/dist/agentic/guides/modules/eudr/index.md +1 -1
- package/dist/agentic/guides/modules/events/index.md +1 -1
- package/dist/agentic/guides/modules/feature_toggles/index.md +1 -1
- package/dist/agentic/guides/modules/gateway_stripe/index.md +1 -1
- package/dist/agentic/guides/modules/generators/index.md +1 -1
- package/dist/agentic/guides/modules/inbox_ops/index.md +1 -1
- package/dist/agentic/guides/modules/integrations/index.md +1 -1
- package/dist/agentic/guides/modules/messages/index.md +1 -1
- package/dist/agentic/guides/modules/notifications/index.md +1 -1
- package/dist/agentic/guides/modules/onboarding/index.md +1 -1
- package/dist/agentic/guides/modules/payment_gateways/index.md +1 -1
- package/dist/agentic/guides/modules/perspectives/index.md +1 -1
- package/dist/agentic/guides/modules/phone_calls/index.md +1 -1
- package/dist/agentic/guides/modules/planner/index.md +1 -1
- package/dist/agentic/guides/modules/portal/index.md +1 -1
- package/dist/agentic/guides/modules/progress/index.md +1 -1
- package/dist/agentic/guides/modules/push_notifications/index.md +1 -1
- package/dist/agentic/guides/modules/query_index/index.md +1 -1
- package/dist/agentic/guides/modules/record_locks/index.md +1 -1
- package/dist/agentic/guides/modules/resources/index.md +1 -1
- package/dist/agentic/guides/modules/sales/index.md +1 -1
- package/dist/agentic/guides/modules/scheduler/index.md +1 -1
- package/dist/agentic/guides/modules/search/index.md +1 -1
- package/dist/agentic/guides/modules/security/index.md +1 -1
- package/dist/agentic/guides/modules/seeds/cli-commands.md +14 -0
- package/dist/agentic/guides/modules/seeds/exact-override-targets.md +14 -0
- package/dist/agentic/guides/modules/seeds/index.md +14 -0
- package/dist/agentic/guides/modules/seeds/owned-contract-module-metadata.md +11 -0
- package/dist/agentic/guides/modules/shipping_carriers/index.md +1 -1
- package/dist/agentic/guides/modules/sso/index.md +1 -1
- package/dist/agentic/guides/modules/staff/index.md +1 -1
- package/dist/agentic/guides/modules/storage_s3/index.md +1 -1
- package/dist/agentic/guides/modules/sync_akeneo/index.md +1 -1
- package/dist/agentic/guides/modules/sync_excel/index.md +1 -1
- package/dist/agentic/guides/modules/system_status_overlays/index.md +1 -1
- package/dist/agentic/guides/modules/tillio/index.md +1 -1
- package/dist/agentic/guides/modules/translations/index.md +1 -1
- package/dist/agentic/guides/modules/warranty_claims/index.md +1 -1
- package/dist/agentic/guides/modules/webhooks/index.md +1 -1
- package/dist/agentic/guides/modules/wms/index.md +1 -1
- package/dist/agentic/guides/modules/workflows/acl-features.md +11 -1
- package/dist/agentic/guides/modules/workflows/active-extension-bindings.md +4 -1
- package/dist/agentic/guides/modules/workflows/ai-tools-mcp-capabilities.md +17 -0
- package/dist/agentic/guides/modules/workflows/backend-pages.md +3 -0
- package/dist/agentic/guides/modules/workflows/contribution-resolutions.md +24 -0
- package/dist/agentic/guides/modules/workflows/di-registrations-rich.md +11 -7
- package/dist/agentic/guides/modules/workflows/di-service-tokens.md +11 -7
- package/dist/agentic/guides/modules/workflows/domain-commands.md +11 -0
- package/dist/agentic/guides/modules/workflows/entities.md +2 -0
- package/dist/agentic/guides/modules/workflows/events.md +14 -8
- package/dist/agentic/guides/modules/workflows/exact-override-targets.md +45 -10
- package/dist/agentic/guides/modules/workflows/frontend-pages.md +2 -0
- package/dist/agentic/guides/modules/workflows/incoming-installed-contributions.md +17 -0
- package/dist/agentic/guides/modules/workflows/index.md +15 -12
- package/dist/agentic/guides/modules/workflows/notifications.md +2 -0
- package/dist/agentic/guides/modules/workflows/setup.md +1 -1
- package/dist/agentic/guides/modules/workflows/umes-contributions.md +24 -0
- package/dist/agentic/guides/modules/workflows/umes-hosts.md +14 -6
- package/dist/agentic/guides/modules/workflows/workers.md +4 -1
- package/dist/agentic/guides/reference-module-facts.json +2 -2
- package/dist/agentic/guides/reference-modules/example/index.md +1 -1
- package/dist/agentic/guides/upstream/AGENTS.md +3 -1
- package/dist/agentic/guides/upstream/manifest.json +2 -2
- package/dist/agentic/shared/ai/harness/design-system-inventory.json +118 -106
- package/dist/agentic/shared/ai/harness/source-link-inventory.json +8 -8
- package/dist/lib/agentic-init.js +4 -0
- package/dist/lib/agentic-init.js.map +2 -2
- package/dist/lib/agentic-setup.js +19 -5
- package/dist/lib/agentic-setup.js.map +2 -2
- package/dist/lib/generators/extensions/agent-files.js +913 -0
- package/dist/lib/generators/extensions/agent-files.js.map +7 -0
- package/dist/lib/generators/extensions/index.js +2 -0
- package/dist/lib/generators/extensions/index.js.map +2 -2
- package/dist/lib/generators/index.js +3 -1
- package/dist/lib/generators/index.js.map +2 -2
- package/dist/lib/generators/module-registry.js +40 -11
- package/dist/lib/generators/module-registry.js.map +2 -2
- package/dist/lib/generators/route-collisions.js +35 -0
- package/dist/lib/generators/route-collisions.js.map +7 -0
- package/dist/lib/generators/web-research-adapters.js +95 -0
- package/dist/lib/generators/web-research-adapters.js.map +7 -0
- package/dist/mercato.js +5 -2
- package/dist/mercato.js.map +2 -2
- package/package.json +6 -6
- package/src/__tests__/agent-files.test.ts +199 -0
- package/src/__tests__/mercato.test.ts +14 -0
- package/src/lib/__tests__/agentic-init.test.ts +11 -0
- package/src/lib/__tests__/agentic-setup.ownership.test.ts +5 -0
- package/src/lib/agentic-init.ts +4 -0
- package/src/lib/agentic-setup.ts +30 -5
- package/src/lib/generators/__tests__/agent-files-extension.test.ts +477 -0
- package/src/lib/generators/__tests__/module-facts.bc-guard.test.ts +25 -8
- package/src/lib/generators/__tests__/route-collisions.test.ts +155 -0
- package/src/lib/generators/extensions/agent-files.ts +1332 -0
- package/src/lib/generators/extensions/index.ts +2 -0
- package/src/lib/generators/index.ts +1 -0
- package/src/lib/generators/module-registry.ts +66 -17
- package/src/lib/generators/route-collisions.ts +57 -0
- package/src/lib/generators/web-research-adapters.ts +133 -0
- package/src/mercato.ts +4 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/cli",
|
|
3
|
-
"version": "0.7.1-develop.
|
|
3
|
+
"version": "0.7.1-develop.7194.1.ab4fc81f82",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -60,9 +60,9 @@
|
|
|
60
60
|
"@mikro-orm/decorators": "^7.1.14",
|
|
61
61
|
"@mikro-orm/migrations": "^7.1.14",
|
|
62
62
|
"@mikro-orm/postgresql": "^7.1.14",
|
|
63
|
-
"@open-mercato/queue": "0.7.1-develop.
|
|
64
|
-
"@open-mercato/shared": "0.7.1-develop.
|
|
65
|
-
"@open-mercato/telemetry": "0.7.1-develop.
|
|
63
|
+
"@open-mercato/queue": "0.7.1-develop.7194.1.ab4fc81f82",
|
|
64
|
+
"@open-mercato/shared": "0.7.1-develop.7194.1.ab4fc81f82",
|
|
65
|
+
"@open-mercato/telemetry": "0.7.1-develop.7194.1.ab4fc81f82",
|
|
66
66
|
"cross-spawn": "^7.0.6",
|
|
67
67
|
"pg": "8.23.0",
|
|
68
68
|
"semver": "^7.8.5",
|
|
@@ -73,10 +73,10 @@
|
|
|
73
73
|
"typescript-js": "npm:typescript@6.0.3"
|
|
74
74
|
},
|
|
75
75
|
"peerDependencies": {
|
|
76
|
-
"@open-mercato/shared": "0.7.1-develop.
|
|
76
|
+
"@open-mercato/shared": "0.7.1-develop.7194.1.ab4fc81f82"
|
|
77
77
|
},
|
|
78
78
|
"devDependencies": {
|
|
79
|
-
"@open-mercato/shared": "0.7.1-develop.
|
|
79
|
+
"@open-mercato/shared": "0.7.1-develop.7194.1.ab4fc81f82",
|
|
80
80
|
"@types/jest": "^30.0.0",
|
|
81
81
|
"jest": "^30.4.2",
|
|
82
82
|
"ts-jest": "^29.4.12"
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import os from 'node:os'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import { createAgentFilesExtension } from '../lib/generators/extensions/agent-files'
|
|
5
|
+
import type { ModuleScanContext } from '../lib/generators/extension'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Build a throwaway repo root carrying the `docker/opencode` + `packages`
|
|
9
|
+
* sentinels the extension uses to locate the repo, plus a module `agents/` tree
|
|
10
|
+
* under the app base. Returns the paths the extension's fs side effect targets.
|
|
11
|
+
*/
|
|
12
|
+
function makeRepo(): {
|
|
13
|
+
root: string
|
|
14
|
+
appBase: string
|
|
15
|
+
pkgBase: string
|
|
16
|
+
dockerAgentsDir: string
|
|
17
|
+
manifestPath: string
|
|
18
|
+
} {
|
|
19
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'agent-files-repo-'))
|
|
20
|
+
fs.mkdirSync(path.join(root, 'docker', 'opencode'), { recursive: true })
|
|
21
|
+
fs.mkdirSync(path.join(root, 'packages'), { recursive: true })
|
|
22
|
+
const appBase = path.join(root, 'apps', 'mercato', 'src', 'modules', 'agent_examples')
|
|
23
|
+
const pkgBase = path.join(root, 'packages', 'core', 'src', 'modules', '__none__')
|
|
24
|
+
fs.mkdirSync(appBase, { recursive: true })
|
|
25
|
+
fs.mkdirSync(pkgBase, { recursive: true })
|
|
26
|
+
return {
|
|
27
|
+
root,
|
|
28
|
+
appBase,
|
|
29
|
+
pkgBase,
|
|
30
|
+
dockerAgentsDir: path.join(root, 'docker', 'opencode', 'agents'),
|
|
31
|
+
manifestPath: path.join(
|
|
32
|
+
root,
|
|
33
|
+
'packages',
|
|
34
|
+
'enterprise',
|
|
35
|
+
'src',
|
|
36
|
+
'modules',
|
|
37
|
+
'agent_orchestrator',
|
|
38
|
+
'generated',
|
|
39
|
+
'file-agents.generated.ts',
|
|
40
|
+
),
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function writeAgent(
|
|
45
|
+
agentDir: string,
|
|
46
|
+
files: { claude: string; outcome: string },
|
|
47
|
+
): void {
|
|
48
|
+
fs.mkdirSync(agentDir, { recursive: true })
|
|
49
|
+
fs.writeFileSync(path.join(agentDir, 'AGENT.md'), files.claude, 'utf8')
|
|
50
|
+
fs.writeFileSync(path.join(agentDir, 'OUTCOME.md'), files.outcome, 'utf8')
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const PRIMARY_CLAUDE = [
|
|
54
|
+
'---',
|
|
55
|
+
'id: deals.health_check',
|
|
56
|
+
'label: Deal health check',
|
|
57
|
+
'description: Assess a deal and propose the next stage.',
|
|
58
|
+
'subAgents: [deals.activity_scan]',
|
|
59
|
+
'---',
|
|
60
|
+
'You assess deal health.',
|
|
61
|
+
].join('\n')
|
|
62
|
+
|
|
63
|
+
const PRIMARY_OUTCOME = [
|
|
64
|
+
'---',
|
|
65
|
+
'kind: proposal',
|
|
66
|
+
'---',
|
|
67
|
+
'```json',
|
|
68
|
+
JSON.stringify({
|
|
69
|
+
type: 'object',
|
|
70
|
+
required: ['rationale'],
|
|
71
|
+
properties: { rationale: { type: 'string', minLength: 1 } },
|
|
72
|
+
}),
|
|
73
|
+
'```',
|
|
74
|
+
].join('\n')
|
|
75
|
+
|
|
76
|
+
const SUB_CLAUDE = [
|
|
77
|
+
'---',
|
|
78
|
+
'id: deals.activity_scan',
|
|
79
|
+
'label: Activity scan',
|
|
80
|
+
'description: Scan recent deal activity.',
|
|
81
|
+
'---',
|
|
82
|
+
'You scan activity.',
|
|
83
|
+
].join('\n')
|
|
84
|
+
|
|
85
|
+
const SUB_OUTCOME = [
|
|
86
|
+
'---',
|
|
87
|
+
'kind: research',
|
|
88
|
+
'---',
|
|
89
|
+
'```json',
|
|
90
|
+
JSON.stringify({
|
|
91
|
+
type: 'object',
|
|
92
|
+
required: ['momentum'],
|
|
93
|
+
properties: { momentum: { type: 'string', minLength: 1 } },
|
|
94
|
+
}),
|
|
95
|
+
'```',
|
|
96
|
+
].join('\n')
|
|
97
|
+
|
|
98
|
+
function makeCtx(repo: ReturnType<typeof makeRepo>, moduleId: string): ModuleScanContext {
|
|
99
|
+
return {
|
|
100
|
+
moduleId,
|
|
101
|
+
roots: { appBase: repo.appBase, pkgBase: repo.pkgBase },
|
|
102
|
+
imps: {} as ModuleScanContext['imps'],
|
|
103
|
+
importIdRef: { value: 0 },
|
|
104
|
+
sharedImports: [],
|
|
105
|
+
resolveModuleFile: (() => null) as unknown as ModuleScanContext['resolveModuleFile'],
|
|
106
|
+
resolveFirstModuleFile: (() => null) as unknown as ModuleScanContext['resolveFirstModuleFile'],
|
|
107
|
+
processStandaloneConfig: () => null,
|
|
108
|
+
sanitizeGeneratedModuleSpecifier: (importPath: string) => importPath,
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
describe('agent-files generator (Phase 4 sub-agents)', () => {
|
|
113
|
+
const created: string[] = []
|
|
114
|
+
afterAll(() => {
|
|
115
|
+
for (const root of created) fs.rmSync(root, { recursive: true, force: true })
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
it('emits a subagent .md, wires the primary task permission, and nests the manifest descriptor', () => {
|
|
119
|
+
const repo = makeRepo()
|
|
120
|
+
created.push(repo.root)
|
|
121
|
+
const agentDir = path.join(repo.appBase, 'agents', 'deals_health_check')
|
|
122
|
+
writeAgent(agentDir, { claude: PRIMARY_CLAUDE, outcome: PRIMARY_OUTCOME })
|
|
123
|
+
writeAgent(path.join(agentDir, 'sub-agents', 'activity_scan'), {
|
|
124
|
+
claude: SUB_CLAUDE,
|
|
125
|
+
outcome: SUB_OUTCOME,
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
const extension = createAgentFilesExtension()
|
|
129
|
+
extension.scanModule(makeCtx(repo, 'agent_examples'))
|
|
130
|
+
extension.generateOutput()
|
|
131
|
+
|
|
132
|
+
const primaryMd = fs.readFileSync(
|
|
133
|
+
path.join(repo.dockerAgentsDir, 'deals_health_check.md'),
|
|
134
|
+
'utf8',
|
|
135
|
+
)
|
|
136
|
+
const subMd = fs.readFileSync(path.join(repo.dockerAgentsDir, 'deals_activity_scan.md'), 'utf8')
|
|
137
|
+
|
|
138
|
+
// Sub-agent file is mode: subagent, read-only, and may NOT delegate (task deny).
|
|
139
|
+
expect(subMd).toContain('mode: subagent')
|
|
140
|
+
expect(subMd).toContain('write: deny')
|
|
141
|
+
expect(subMd).toContain('task: deny')
|
|
142
|
+
expect(subMd).not.toContain('"task": true')
|
|
143
|
+
|
|
144
|
+
// Primary allows the task tool and whitelists ONLY its sub-agent's name.
|
|
145
|
+
expect(primaryMd).toContain('mode: primary')
|
|
146
|
+
// Delegation runs through the orchestrator's own MCP tool, never OpenCode's
|
|
147
|
+
// native `task`: the server-side path is depth-capped, read-only and takes
|
|
148
|
+
// its scope from the run context rather than from model-supplied input. So
|
|
149
|
+
// `task` stays denied on the primary agent too, and the generated allowlist
|
|
150
|
+
// names the delegate tool instead.
|
|
151
|
+
expect(primaryMd).toContain('"open-mercato_agent_orchestrator_delegate_agent": true')
|
|
152
|
+
expect(primaryMd).toContain('task: deny')
|
|
153
|
+
expect(primaryMd).not.toContain('"task": true')
|
|
154
|
+
expect(primaryMd).toContain('## Sub-agents')
|
|
155
|
+
|
|
156
|
+
// Manifest carries the sub-agent as a nested descriptor.
|
|
157
|
+
const manifest = fs.readFileSync(repo.manifestPath, 'utf8')
|
|
158
|
+
expect(manifest).toContain('deals.health_check')
|
|
159
|
+
expect(manifest).toContain('subAgentDescriptors')
|
|
160
|
+
expect(manifest).toContain('deals.activity_scan')
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
it('fails generation when a sub-agent is proposal (only the primary proposes)', () => {
|
|
164
|
+
const repo = makeRepo()
|
|
165
|
+
created.push(repo.root)
|
|
166
|
+
const agentDir = path.join(repo.appBase, 'agents', 'deals_health_check')
|
|
167
|
+
writeAgent(agentDir, { claude: PRIMARY_CLAUDE, outcome: PRIMARY_OUTCOME })
|
|
168
|
+
writeAgent(path.join(agentDir, 'sub-agents', 'bad'), {
|
|
169
|
+
claude: SUB_CLAUDE,
|
|
170
|
+
// proposal sub-agent — must be rejected
|
|
171
|
+
outcome: PRIMARY_OUTCOME,
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
const extension = createAgentFilesExtension()
|
|
175
|
+
expect(() => extension.scanModule(makeCtx(repo, 'agent_examples'))).toThrow(/research/i)
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
it('fails generation when a sub-agent declares its own subAgents (depth cap = 1)', () => {
|
|
179
|
+
const repo = makeRepo()
|
|
180
|
+
created.push(repo.root)
|
|
181
|
+
const agentDir = path.join(repo.appBase, 'agents', 'deals_health_check')
|
|
182
|
+
writeAgent(agentDir, { claude: PRIMARY_CLAUDE, outcome: PRIMARY_OUTCOME })
|
|
183
|
+
writeAgent(path.join(agentDir, 'sub-agents', 'nested'), {
|
|
184
|
+
claude: [
|
|
185
|
+
'---',
|
|
186
|
+
'id: deals.nested',
|
|
187
|
+
'label: Nested',
|
|
188
|
+
'description: Nested sub-agent.',
|
|
189
|
+
'subAgents: [deals.deeper]',
|
|
190
|
+
'---',
|
|
191
|
+
'body',
|
|
192
|
+
].join('\n'),
|
|
193
|
+
outcome: SUB_OUTCOME,
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
const extension = createAgentFilesExtension()
|
|
197
|
+
expect(() => extension.scanModule(makeCtx(repo, 'agent_examples'))).toThrow(/depth cap/i)
|
|
198
|
+
})
|
|
199
|
+
})
|
|
@@ -359,6 +359,7 @@ describe('init command failure output', () => {
|
|
|
359
359
|
generateModuleEntities: jest.fn().mockResolvedValue(undefined),
|
|
360
360
|
generateModuleDi: jest.fn().mockResolvedValue(undefined),
|
|
361
361
|
generateModulePackageSources: jest.fn().mockResolvedValue(undefined),
|
|
362
|
+
generateWebResearchAdapters: jest.fn().mockResolvedValue(undefined),
|
|
362
363
|
generateOpenApi: jest.fn().mockResolvedValue(undefined),
|
|
363
364
|
}))
|
|
364
365
|
jest.doMock('../lib/resolver', () => ({
|
|
@@ -405,6 +406,7 @@ describe('init command failure output', () => {
|
|
|
405
406
|
generateModuleEntities: jest.fn().mockResolvedValue(undefined),
|
|
406
407
|
generateModuleDi: jest.fn().mockResolvedValue(undefined),
|
|
407
408
|
generateModulePackageSources: jest.fn().mockResolvedValue(undefined),
|
|
409
|
+
generateWebResearchAdapters: jest.fn().mockResolvedValue(undefined),
|
|
408
410
|
generateOpenApi: jest.fn().mockResolvedValue(undefined),
|
|
409
411
|
}))
|
|
410
412
|
jest.doMock('../lib/resolver', () => ({
|
|
@@ -454,6 +456,7 @@ describe('init command failure output', () => {
|
|
|
454
456
|
generateModuleEntities: jest.fn().mockResolvedValue(undefined),
|
|
455
457
|
generateModuleDi: jest.fn().mockResolvedValue(undefined),
|
|
456
458
|
generateModulePackageSources: jest.fn().mockResolvedValue(undefined),
|
|
459
|
+
generateWebResearchAdapters: jest.fn().mockResolvedValue(undefined),
|
|
457
460
|
generateOpenApi: jest.fn().mockResolvedValue(undefined),
|
|
458
461
|
}))
|
|
459
462
|
jest.doMock('../lib/db', () => ({
|
|
@@ -554,6 +557,7 @@ describe('generate post-step structural invalidation', () => {
|
|
|
554
557
|
const generateModuleEntities = jest.fn().mockResolvedValue(undefined)
|
|
555
558
|
const generateModuleDi = jest.fn().mockResolvedValue(undefined)
|
|
556
559
|
const generateModulePackageSources = jest.fn().mockResolvedValue(undefined)
|
|
560
|
+
const generateWebResearchAdapters = jest.fn().mockResolvedValue(undefined)
|
|
557
561
|
const generateOpenApi = jest.fn().mockResolvedValue(undefined)
|
|
558
562
|
const invalidate = jest.fn().mockResolvedValue({
|
|
559
563
|
cacheEntriesDeleted: 2,
|
|
@@ -568,6 +572,7 @@ describe('generate post-step structural invalidation', () => {
|
|
|
568
572
|
generateModuleEntities,
|
|
569
573
|
generateModuleDi,
|
|
570
574
|
generateModulePackageSources,
|
|
575
|
+
generateWebResearchAdapters,
|
|
571
576
|
generateOpenApi,
|
|
572
577
|
}))
|
|
573
578
|
jest.doMock('../lib/resolver', () => ({
|
|
@@ -610,6 +615,7 @@ describe('generate post-step structural invalidation', () => {
|
|
|
610
615
|
generateModuleDi: jest.fn().mockResolvedValue(unchangedResult),
|
|
611
616
|
generateModulePackageSources: jest.fn().mockResolvedValue(unchangedResult),
|
|
612
617
|
generateOpenApi: jest.fn().mockResolvedValue(unchangedResult),
|
|
618
|
+
generateWebResearchAdapters: jest.fn().mockResolvedValue(unchangedResult),
|
|
613
619
|
}
|
|
614
620
|
const invalidate = jest.fn()
|
|
615
621
|
|
|
@@ -649,6 +655,7 @@ describe('generate post-step structural invalidation', () => {
|
|
|
649
655
|
const generateModuleEntities = jest.fn().mockResolvedValue(undefined)
|
|
650
656
|
const generateModuleDi = jest.fn().mockResolvedValue(undefined)
|
|
651
657
|
const generateModulePackageSources = jest.fn().mockResolvedValue(undefined)
|
|
658
|
+
const generateWebResearchAdapters = jest.fn().mockResolvedValue(undefined)
|
|
652
659
|
const generateOpenApi = jest.fn().mockResolvedValue(undefined)
|
|
653
660
|
const invalidate = jest.fn().mockRejectedValue(new Error('cache maintenance unavailable'))
|
|
654
661
|
|
|
@@ -658,6 +665,7 @@ describe('generate post-step structural invalidation', () => {
|
|
|
658
665
|
generateModuleEntities,
|
|
659
666
|
generateModuleDi,
|
|
660
667
|
generateModulePackageSources,
|
|
668
|
+
generateWebResearchAdapters,
|
|
661
669
|
generateOpenApi,
|
|
662
670
|
}))
|
|
663
671
|
jest.doMock('../lib/resolver', () => ({
|
|
@@ -695,6 +703,7 @@ describe('generate post-step structural invalidation', () => {
|
|
|
695
703
|
generateModuleDi: generate,
|
|
696
704
|
generateModulePackageSources: generate,
|
|
697
705
|
generateOpenApi: generate,
|
|
706
|
+
generateWebResearchAdapters: generate,
|
|
698
707
|
}))
|
|
699
708
|
jest.doMock('../lib/resolver', () => ({
|
|
700
709
|
createResolver: () => ({
|
|
@@ -800,6 +809,7 @@ describe('server dev managed process exits', () => {
|
|
|
800
809
|
})
|
|
801
810
|
jest.doMock('../lib/generators', () => ({
|
|
802
811
|
generateModulePackageSources: jest.fn().mockResolvedValue(undefined),
|
|
812
|
+
generateWebResearchAdapters: jest.fn().mockResolvedValue(undefined),
|
|
803
813
|
}))
|
|
804
814
|
jest.doMock('../lib/resolver', () => ({
|
|
805
815
|
resolveEnvironment: () => ({
|
|
@@ -841,6 +851,7 @@ describe('server dev managed process exits', () => {
|
|
|
841
851
|
})
|
|
842
852
|
jest.doMock('../lib/generators', () => ({
|
|
843
853
|
generateModulePackageSources: jest.fn().mockResolvedValue(undefined),
|
|
854
|
+
generateWebResearchAdapters: jest.fn().mockResolvedValue(undefined),
|
|
844
855
|
}))
|
|
845
856
|
jest.doMock('../lib/resolver', () => ({
|
|
846
857
|
resolveEnvironment: () => ({
|
|
@@ -1209,6 +1220,7 @@ describe('server dev managed process exits', () => {
|
|
|
1209
1220
|
})
|
|
1210
1221
|
jest.doMock('../lib/generators', () => ({
|
|
1211
1222
|
generateModulePackageSources: jest.fn().mockResolvedValue(undefined),
|
|
1223
|
+
generateWebResearchAdapters: jest.fn().mockResolvedValue(undefined),
|
|
1212
1224
|
}))
|
|
1213
1225
|
jest.doMock('../lib/resolver', () => ({
|
|
1214
1226
|
resolveEnvironment: () => ({
|
|
@@ -1363,6 +1375,7 @@ describe('server dev managed process exits', () => {
|
|
|
1363
1375
|
})
|
|
1364
1376
|
jest.doMock('../lib/generators', () => ({
|
|
1365
1377
|
generateModulePackageSources: jest.fn().mockResolvedValue(undefined),
|
|
1378
|
+
generateWebResearchAdapters: jest.fn().mockResolvedValue(undefined),
|
|
1366
1379
|
}))
|
|
1367
1380
|
jest.doMock('../lib/resolver', () => ({
|
|
1368
1381
|
resolveEnvironment: () => ({
|
|
@@ -1643,6 +1656,7 @@ describe('server start managed process exits', () => {
|
|
|
1643
1656
|
}))
|
|
1644
1657
|
jest.doMock('../lib/generators', () => ({
|
|
1645
1658
|
generateModulePackageSources: jest.fn().mockResolvedValue(undefined),
|
|
1659
|
+
generateWebResearchAdapters: jest.fn().mockResolvedValue(undefined),
|
|
1646
1660
|
}))
|
|
1647
1661
|
jest.doMock('../lib/resolver', () => ({
|
|
1648
1662
|
resolveEnvironment: () => ({
|
|
@@ -94,6 +94,13 @@ describe('resolveRelevantAgenticFiles', () => {
|
|
|
94
94
|
])
|
|
95
95
|
})
|
|
96
96
|
|
|
97
|
+
it('resolves github-copilot existing files', () => {
|
|
98
|
+
expect(loadActualModule().resolveRelevantAgenticFiles('github-copilot')).toEqual([
|
|
99
|
+
'.github/copilot-instructions.md',
|
|
100
|
+
'.vscode/mcp.json.example',
|
|
101
|
+
])
|
|
102
|
+
})
|
|
103
|
+
|
|
97
104
|
it('falls back to the full known file list when no tool is provided', () => {
|
|
98
105
|
expect(loadActualModule().resolveRelevantAgenticFiles()).toEqual([
|
|
99
106
|
'CLAUDE.md',
|
|
@@ -101,6 +108,8 @@ describe('resolveRelevantAgenticFiles', () => {
|
|
|
101
108
|
'.mcp.json.example',
|
|
102
109
|
'.codex/mcp.json.example',
|
|
103
110
|
'.cursor/hooks.json',
|
|
111
|
+
'.github/copilot-instructions.md',
|
|
112
|
+
'.vscode/mcp.json.example',
|
|
104
113
|
])
|
|
105
114
|
})
|
|
106
115
|
|
|
@@ -111,6 +120,8 @@ describe('resolveRelevantAgenticFiles', () => {
|
|
|
111
120
|
'.mcp.json.example',
|
|
112
121
|
'.codex/mcp.json.example',
|
|
113
122
|
'.cursor/hooks.json',
|
|
123
|
+
'.github/copilot-instructions.md',
|
|
124
|
+
'.vscode/mcp.json.example',
|
|
114
125
|
])
|
|
115
126
|
})
|
|
116
127
|
})
|
|
@@ -397,9 +397,14 @@ describe('runAgenticSetup ownership modes', () => {
|
|
|
397
397
|
expect(existsSync(join(appDir, '.claude', 'settings.json'))).toBe(true)
|
|
398
398
|
expect(existsSync(join(appDir, '.codex', 'mcp.json.example'))).toBe(true)
|
|
399
399
|
expect(existsSync(join(appDir, '.cursor', 'hooks.json'))).toBe(false)
|
|
400
|
+
expect(existsSync(join(appDir, '.github', 'copilot-instructions.md'))).toBe(false)
|
|
400
401
|
const tiers = JSON.parse(readFileSync(join(appDir, '.ai', 'skills', 'tiers.json'), 'utf8')) as {
|
|
401
402
|
agents?: { ignore?: string[] }
|
|
402
403
|
}
|
|
404
|
+
// Every SKILL-MANAGED tool the app did not select is ignored, so the skills
|
|
405
|
+
// installer skips its links. GitHub Copilot is deliberately absent: it reads
|
|
406
|
+
// `.github/` instruction files rather than a skills directory, and the
|
|
407
|
+
// installer rejects its id as an unknown agent.
|
|
403
408
|
expect(tiers.agents?.ignore).toEqual(['cursor'])
|
|
404
409
|
})
|
|
405
410
|
})
|
package/src/lib/agentic-init.ts
CHANGED
|
@@ -21,6 +21,10 @@ const TOOL_EXISTING_FILES: Record<string, string[]> = {
|
|
|
21
21
|
'cursor': [
|
|
22
22
|
'.cursor/hooks.json',
|
|
23
23
|
],
|
|
24
|
+
'github-copilot': [
|
|
25
|
+
'.github/copilot-instructions.md',
|
|
26
|
+
'.vscode/mcp.json.example',
|
|
27
|
+
],
|
|
24
28
|
}
|
|
25
29
|
|
|
26
30
|
function parseArgs(args: string[]): AgenticInitOptions {
|
package/src/lib/agentic-setup.ts
CHANGED
|
@@ -811,18 +811,39 @@ function generateCursor(config: AgenticConfig): void {
|
|
|
811
811
|
// which scripts/install-skills.sh populates.
|
|
812
812
|
}
|
|
813
813
|
|
|
814
|
+
function generateGithubCopilot(config: AgenticConfig): void {
|
|
815
|
+
const { targetDir } = config
|
|
816
|
+
const srcDir = join(AGENTIC_DIR, 'github-copilot')
|
|
817
|
+
|
|
818
|
+
writeTemplate(srcDir, 'copilot-instructions.md.template', join(targetDir, '.github', 'copilot-instructions.md'), config)
|
|
819
|
+
copyFile(srcDir, 'instructions/entity-guard.instructions.md', join(targetDir, '.github', 'instructions', 'entity-guard.instructions.md'))
|
|
820
|
+
copyFile(srcDir, 'instructions/generated-guard.instructions.md', join(targetDir, '.github', 'instructions', 'generated-guard.instructions.md'))
|
|
821
|
+
copyFile(srcDir, 'mcp.json.example', join(targetDir, '.vscode', 'mcp.json.example'))
|
|
822
|
+
|
|
823
|
+
// No .github/skills directory: Copilot reads the canonical .agents/skills/,
|
|
824
|
+
// which scripts/install-skills.sh populates.
|
|
825
|
+
}
|
|
814
826
|
// ─── Wizard ──────────────────────────────────────────────────────────────
|
|
815
827
|
|
|
816
828
|
const TOOLS = [
|
|
817
829
|
{ key: '1', label: 'Claude Code (Anthropic)', id: 'claude-code' },
|
|
818
830
|
{ key: '2', label: 'Codex (OpenAI)', id: 'codex' },
|
|
819
831
|
{ key: '3', label: 'Cursor (Anysphere)', id: 'cursor' },
|
|
820
|
-
{ key: '4', label: '
|
|
821
|
-
{ key: '5', label: '
|
|
832
|
+
{ key: '4', label: 'GitHub Copilot (GitHub)', id: 'github-copilot' },
|
|
833
|
+
{ key: '5', label: 'Multiple tools (select individually)', id: 'multiple' },
|
|
834
|
+
{ key: '6', label: 'Skip — set up manually later', id: 'skip' },
|
|
822
835
|
] as const
|
|
823
836
|
|
|
824
837
|
const SELECTABLE = TOOLS.filter((t) => t.id !== 'multiple' && t.id !== 'skip')
|
|
825
838
|
|
|
839
|
+
/**
|
|
840
|
+
* The agents whose SKILL directories `scripts/install-skills.mjs` manages.
|
|
841
|
+
* GitHub Copilot is selectable here but reads instruction files under
|
|
842
|
+
* `.github/`, not a skills directory, so the installer does not know that id —
|
|
843
|
+
* naming it in `agents.ignore` aborts the install with "unknown agent".
|
|
844
|
+
*/
|
|
845
|
+
const SKILL_MANAGED_AGENT_IDS = ['claude-code', 'codex', 'cursor']
|
|
846
|
+
|
|
826
847
|
function persistedAgentSelection(targetDir: string): string[] | null {
|
|
827
848
|
const selectableIds = SELECTABLE.map((tool) => tool.id)
|
|
828
849
|
const tiersPath = join(targetDir, '.ai', 'skills', 'tiers.json')
|
|
@@ -865,9 +886,9 @@ async function promptSelection(ask: AskFn): Promise<string[]> {
|
|
|
865
886
|
|
|
866
887
|
const answer = (await ask(' Enter number(s) separated by comma [1]: ')).trim() || '1'
|
|
867
888
|
|
|
868
|
-
if (answer === '
|
|
889
|
+
if (answer === '6') return ['skip']
|
|
869
890
|
|
|
870
|
-
if (answer === '
|
|
891
|
+
if (answer === '5') {
|
|
871
892
|
const selected: string[] = []
|
|
872
893
|
for (const tool of SELECTABLE) {
|
|
873
894
|
const yn = await ask(` Include ${tool.label}? [y/N]: `)
|
|
@@ -962,6 +983,9 @@ export async function runAgenticSetup(
|
|
|
962
983
|
if (selectedIds.includes('cursor')) {
|
|
963
984
|
console.log(' ✓ Cursor — .cursor/rules/, .cursor/hooks/, .cursor/mcp.json.example')
|
|
964
985
|
}
|
|
986
|
+
if (selectedIds.includes('github-copilot')) {
|
|
987
|
+
console.log(' ✓ GitHub Copilot — .github/copilot-instructions.md, .github/instructions/, .vscode/mcp.json.example')
|
|
988
|
+
}
|
|
965
989
|
if (config.experimentalHooksValidator) {
|
|
966
990
|
console.log(' ✓ Experimental gate-evidence/typecheck validator hooks')
|
|
967
991
|
}
|
|
@@ -978,6 +1002,7 @@ function generateHarness(config: AgenticConfig, selectedIds: string[]): void {
|
|
|
978
1002
|
if (selectedIds.includes('claude-code')) generateClaudeCode(config)
|
|
979
1003
|
if (selectedIds.includes('codex')) generateCodex(config)
|
|
980
1004
|
if (selectedIds.includes('cursor')) generateCursor(config)
|
|
1005
|
+
if (selectedIds.includes('github-copilot')) generateGithubCopilot(config)
|
|
981
1006
|
|
|
982
1007
|
enforceGeneratedRootBudget(config)
|
|
983
1008
|
|
|
@@ -1003,7 +1028,7 @@ function enforceGeneratedRootBudget(
|
|
|
1003
1028
|
function persistAgentSelection(targetDir: string, selectedIds: string[]): void {
|
|
1004
1029
|
const manifestPath = join(targetDir, '.ai', 'skills', 'tiers.json')
|
|
1005
1030
|
if (!existsSync(manifestPath)) return
|
|
1006
|
-
const ignore =
|
|
1031
|
+
const ignore = SKILL_MANAGED_AGENT_IDS.filter((id) => !selectedIds.includes(id))
|
|
1007
1032
|
const manifest = JSON.parse(readFileSync(manifestPath, 'utf-8')) as Record<string, unknown>
|
|
1008
1033
|
if (ignore.length > 0) {
|
|
1009
1034
|
manifest.agents = { ignore }
|