sinapse-ai 1.6.1 → 1.8.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/.claude/CLAUDE.md +5 -11
- package/.claude/hooks/README.md +14 -1
- package/.claude/hooks/code-intel-pretool.cjs +115 -0
- package/.claude/hooks/enforce-delegation.cjs +31 -3
- package/.claude/hooks/enforce-framework-boundary.cjs +324 -0
- package/.claude/hooks/enforce-permission-mode.cjs +249 -0
- package/.claude/hooks/secret-scanning.cjs +34 -43
- package/.claude/hooks/synapse-engine.cjs +23 -23
- package/.claude/hooks/telemetry-post-tool.cjs +128 -0
- package/.claude/hooks/telemetry-stop.cjs +132 -0
- package/.claude/hooks/verify-packages.cjs +9 -2
- package/.claude/rules/documentation-first.md +1 -1
- package/.claude/rules/hook-governance.md +2 -0
- package/.sinapse-ai/cli/commands/health/index.js +24 -0
- package/.sinapse-ai/core/README.md +11 -0
- package/.sinapse-ai/core/config/config-loader.js +19 -0
- package/.sinapse-ai/core/config/merge-utils.js +8 -0
- package/.sinapse-ai/core/errors/constants.js +147 -0
- package/.sinapse-ai/core/errors/error-registry.js +176 -0
- package/.sinapse-ai/core/errors/index.js +50 -0
- package/.sinapse-ai/core/errors/serializer.js +147 -0
- package/.sinapse-ai/core/errors/sinapse-error.js +144 -0
- package/.sinapse-ai/core/errors/utils.js +187 -0
- package/.sinapse-ai/core/execution/build-orchestrator.js +47 -49
- package/.sinapse-ai/core/execution/build-state-manager.js +183 -31
- package/.sinapse-ai/core/execution/parallel-executor.js +7 -1
- package/.sinapse-ai/core/execution/semantic-merge-engine.js +26 -14
- package/.sinapse-ai/core/execution/subagent-dispatcher.js +201 -60
- package/.sinapse-ai/core/execution/wave-executor.js +4 -1
- package/.sinapse-ai/core/grounding/README.md +71 -11
- package/.sinapse-ai/core/health-check/checks/project/framework-config.js +38 -2
- package/.sinapse-ai/core/health-check/checks/project/package-json.js +47 -3
- package/.sinapse-ai/core/health-check/checks/services/gemini-cli.js +117 -0
- package/.sinapse-ai/core/health-check/checks/services/index.js +2 -0
- package/.sinapse-ai/core/health-check/healers/index.js +40 -3
- package/.sinapse-ai/core/ideation/ideation-engine.js +212 -107
- package/.sinapse-ai/core/ids/gate-evaluator.js +318 -0
- package/.sinapse-ai/core/ids/gates/g5-semantic-handshake.js +190 -0
- package/.sinapse-ai/core/ids/gates/g6-ci-integrity.js +162 -0
- package/.sinapse-ai/core/ids/index.js +30 -0
- package/.sinapse-ai/core/memory/__tests__/active-modules.verify.js +11 -0
- package/.sinapse-ai/core/memory/gotchas-memory.js +37 -2
- package/.sinapse-ai/core/orchestration/agent-invoker.js +29 -6
- package/.sinapse-ai/core/orchestration/brownfield-handler.js +36 -3
- package/.sinapse-ai/core/orchestration/condition-evaluator.js +57 -0
- package/.sinapse-ai/core/orchestration/executors/epic-3-executor.js +76 -5
- package/.sinapse-ai/core/orchestration/executors/epic-4-executor.js +63 -17
- package/.sinapse-ai/core/orchestration/executors/epic-6-executor.js +153 -41
- package/.sinapse-ai/core/orchestration/executors/epic-executor.js +40 -0
- package/.sinapse-ai/core/orchestration/greenfield-handler.js +87 -3
- package/.sinapse-ai/core/orchestration/master-orchestrator.js +150 -10
- package/.sinapse-ai/core/orchestration/parallel-executor.js +6 -1
- package/.sinapse-ai/core/orchestration/recovery-handler.js +81 -8
- package/.sinapse-ai/core/orchestration/workflow-executor.js +41 -0
- package/.sinapse-ai/core/registry/registry-loader.js +71 -5
- package/.sinapse-ai/core/registry/squad-agent-resolver.js +253 -0
- package/.sinapse-ai/core/synapse/context/context-tracker.js +104 -9
- package/.sinapse-ai/core/synapse/context/index.js +19 -0
- package/.sinapse-ai/core/synapse/context/semantic-handshake-engine.js +555 -0
- package/.sinapse-ai/core/synapse/diagnostics/collectors/pipeline-collector.js +4 -2
- package/.sinapse-ai/core/synapse/engine.js +43 -3
- package/.sinapse-ai/core/telemetry/ids-sink.js +188 -0
- package/.sinapse-ai/core/utils/output-formatter.js +8 -290
- package/.sinapse-ai/core/utils/spawn-safe.js +186 -0
- package/.sinapse-ai/core-config.yaml +68 -1
- package/.sinapse-ai/data/entity-registry.yaml +15082 -13618
- package/.sinapse-ai/data/registry-update-log.jsonl +143 -0
- package/.sinapse-ai/development/agents/developer.md +2 -0
- package/.sinapse-ai/development/agents/devops.md +9 -0
- package/.sinapse-ai/development/external-executors/README.md +18 -0
- package/.sinapse-ai/development/external-executors/codex.md +56 -0
- package/.sinapse-ai/development/scripts/populate-entity-registry.js +65 -9
- package/.sinapse-ai/development/scripts/squad/squad-downloader.js +169 -14
- package/.sinapse-ai/development/tasks/delegate-to-external-executor.md +152 -0
- package/.sinapse-ai/development/tasks/github-devops-pre-push-quality-gate.md +46 -29
- package/.sinapse-ai/development/tasks/update-sinapse.md +3 -3
- package/.sinapse-ai/hooks/sinapse-brand-grounding.cjs +4 -7
- package/.sinapse-ai/hooks/sinapse-ds-grounding.cjs +5 -8
- package/.sinapse-ai/hooks/sinapse-vault-grounding.cjs +6 -9
- package/.sinapse-ai/infrastructure/integrations/ai-providers/ai-provider-factory.js +4 -1
- package/.sinapse-ai/infrastructure/integrations/ai-providers/claude-provider.js +57 -55
- package/.sinapse-ai/infrastructure/integrations/pm-adapters/github-adapter.js +9 -7
- package/.sinapse-ai/infrastructure/scripts/ide-sync/gemini-commands.js +298 -0
- package/.sinapse-ai/infrastructure/scripts/ide-sync/index.js +127 -6
- package/.sinapse-ai/infrastructure/scripts/ide-sync/persona-renderer.js +97 -0
- package/.sinapse-ai/infrastructure/scripts/ide-sync/transformers/antigravity.js +121 -0
- package/.sinapse-ai/infrastructure/scripts/ide-sync/transformers/cursor.js +119 -0
- package/.sinapse-ai/infrastructure/scripts/ide-sync/transformers/github-copilot.js +191 -0
- package/.sinapse-ai/infrastructure/scripts/ide-sync/transformers/kimi.js +448 -0
- package/.sinapse-ai/install-manifest.yaml +218 -114
- package/.sinapse-ai/product/templates/engine/renderer.js +20 -1
- package/.sinapse-ai/scripts/pm.sh +18 -6
- package/bin/cli.js +17 -0
- package/bin/commands/agents.js +96 -0
- package/bin/commands/doctor.js +15 -0
- package/bin/commands/ideate.js +129 -0
- package/bin/commands/uninstall.js +40 -0
- package/bin/postinstall.js +50 -4
- package/bin/sinapse.js +146 -2
- package/bin/utils/secret-scanner-core.js +253 -0
- package/bin/utils/staged-secret-scan.js +106 -40
- package/docs/framework/collaboration-autonomy-plan.md +18 -18
- package/docs/guides/parallel-workflow.md +6 -6
- package/package.json +22 -5
- package/packages/installer/src/installer/git-hooks-installer.js +384 -0
- package/packages/installer/src/installer/sinapse-ai-installer.js +16 -0
- package/packages/installer/src/wizard/ide-config-generator.js +23 -0
- package/packages/installer/src/wizard/validators.js +38 -1
- package/packages/installer/tests/unit/artifact-copy-pipeline/artifact-copy-pipeline.test.js +5 -1
- package/packages/installer/tests/unit/doctor/doctor-checks.test.js +44 -22
- package/packages/installer/tests/unit/git-hooks-installer.test.js +262 -0
- package/scripts/eval-runner.js +422 -0
- package/scripts/generate-install-manifest.js +13 -9
- package/scripts/generate-synapse-runtime.js +51 -0
- package/scripts/regenerate-orqx-stubs.ps1 +6 -5
- package/scripts/validate-all.js +1 -0
- package/scripts/validate-evals.js +466 -0
- package/scripts/validate-schemas.js +539 -0
- package/scripts/validate-squad-orqx.js +9 -2
- package/squads/claude-code-mastery/knowledge-base/memory-systems-reference.md +1 -1
- package/squads/squad-brand/templates/client-delivery-template.md +1 -1
- package/squads/squad-content/knowledge-base/social-compression-framework.md +1 -1
- package/squads/squad-council/knowledge-base/brand-strategy-models.md +1 -1
- package/.sinapse-ai/development/scripts/elicitation-engine.js +0 -385
- package/.sinapse-ai/development/scripts/elicitation-session-manager.js +0 -300
- package/.sinapse-ai/development/tasks/test-validation-task.md +0 -172
- package/docs/chrome-brain-upgrade-plan.md +0 -624
- package/docs/constitution-compliance.md +0 -87
- package/docs/mega-upgrade-orchestration-plan.md +0 -71
- package/docs/research-synthesis-for-upgrade.md +0 -511
- package/docs/security-audit-report.md +0 -306
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persona Renderer — shared across IDE transformers (cursor, antigravity,
|
|
3
|
+
* github-copilot).
|
|
4
|
+
*
|
|
5
|
+
* PARIDADE-IDE-002 (audit 2026-06-11): the transformers used to emit only
|
|
6
|
+
* name/title/whenToUse + a few commands — ~8% of the agent. The agent's actual
|
|
7
|
+
* operating contract (role, identity, style, focus, core principles) was
|
|
8
|
+
* dropped, so the IDE stub couldn't reason like the agent. This renderer pulls
|
|
9
|
+
* that content from the parsed YAML and emits it as portable Markdown so every
|
|
10
|
+
* IDE keeps the persona that matters. Pure data → markdown; no side effects.
|
|
11
|
+
*
|
|
12
|
+
* @module ide-sync/persona-renderer
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
'use strict';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Render the core_principles array. Each item is either a string or a
|
|
19
|
+
* single-key object like { CRITICAL: '…' } (the agent YAML convention).
|
|
20
|
+
* @param {Array} principles
|
|
21
|
+
* @param {number} [limit=12] - Cap to keep IDE rules lean.
|
|
22
|
+
* @returns {string} Markdown bullet list (empty string when none).
|
|
23
|
+
*/
|
|
24
|
+
function renderCorePrinciples(principles, limit = 12) {
|
|
25
|
+
if (!Array.isArray(principles) || principles.length === 0) return '';
|
|
26
|
+
const lines = [];
|
|
27
|
+
for (const p of principles) {
|
|
28
|
+
if (lines.length >= limit) break;
|
|
29
|
+
if (typeof p === 'string') {
|
|
30
|
+
if (p.trim()) lines.push(`- ${p.trim()}`);
|
|
31
|
+
} else if (p && typeof p === 'object') {
|
|
32
|
+
for (const [k, v] of Object.entries(p)) {
|
|
33
|
+
lines.push(`- **${k}:** ${String(v).trim()}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return lines.join('\n');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Render the persona block (role/identity/style/focus) + core principles for an
|
|
42
|
+
* agent. Returns portable Markdown sections, or '' when the agent has no rich
|
|
43
|
+
* YAML (e.g. plain-header agents) — callers keep their existing minimal output.
|
|
44
|
+
*
|
|
45
|
+
* @param {object} agentData - Parsed agent data (from agent-parser).
|
|
46
|
+
* @param {object} [opts]
|
|
47
|
+
* @param {boolean} [opts.includeCommunication=false] - Also emit tone/vocabulary.
|
|
48
|
+
* @returns {string} Markdown (possibly empty).
|
|
49
|
+
*/
|
|
50
|
+
function renderPersona(agentData, opts = {}) {
|
|
51
|
+
const yaml = (agentData && agentData.yaml) || {};
|
|
52
|
+
const persona = yaml.persona || {};
|
|
53
|
+
const profile = (agentData && agentData.persona_profile) || yaml.persona_profile || {};
|
|
54
|
+
|
|
55
|
+
let md = '';
|
|
56
|
+
|
|
57
|
+
const hasPersona = persona.role || persona.identity || persona.style || persona.focus;
|
|
58
|
+
if (hasPersona) {
|
|
59
|
+
md += '## Persona\n\n';
|
|
60
|
+
if (persona.role) md += `**Role:** ${persona.role}\n\n`;
|
|
61
|
+
if (persona.identity) md += `**Identity:** ${persona.identity}\n\n`;
|
|
62
|
+
if (persona.style) md += `**Style:** ${persona.style}\n\n`;
|
|
63
|
+
if (persona.focus) md += `**Focus:** ${persona.focus}\n\n`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const principles = renderCorePrinciples(yaml.core_principles);
|
|
67
|
+
if (principles) {
|
|
68
|
+
md += `## Core Principles\n\n${principles}\n\n`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (opts.includeCommunication && profile.communication) {
|
|
72
|
+
const c = profile.communication;
|
|
73
|
+
const bits = [];
|
|
74
|
+
if (c.tone) bits.push(`tone ${c.tone}`);
|
|
75
|
+
if (Array.isArray(c.vocabulary) && c.vocabulary.length) {
|
|
76
|
+
bits.push(`vocabulary: ${c.vocabulary.slice(0, 8).join(', ')}`);
|
|
77
|
+
}
|
|
78
|
+
if (bits.length) md += `## Voice\n\n${bits.join(' · ')}\n\n`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return md;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Rough persona-retention estimate (chars of rendered persona vs raw file).
|
|
86
|
+
* Used by tests to assert the IDE stub keeps materially more than the old ~8%.
|
|
87
|
+
* @param {object} agentData
|
|
88
|
+
* @returns {number} ratio 0..1
|
|
89
|
+
*/
|
|
90
|
+
function estimateRetention(agentData) {
|
|
91
|
+
const raw = (agentData && agentData.raw) || '';
|
|
92
|
+
if (!raw) return 0;
|
|
93
|
+
const rendered = renderPersona(agentData, { includeCommunication: true });
|
|
94
|
+
return rendered.length / raw.length;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
module.exports = { renderPersona, renderCorePrinciples, estimateRetention };
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Antigravity Transformer - Cursor-style format
|
|
3
|
+
* @story 5.1 - IDE Sync Expansion
|
|
4
|
+
*
|
|
5
|
+
* Format: Similar to Cursor, condensed rules format (.md, no frontmatter)
|
|
6
|
+
* Includes a full "All Commands" section in addition to Quick/Key sections.
|
|
7
|
+
* Target: .antigravity/rules/agents/*.md
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const { getVisibleCommands, normalizeCommands } = require('../agent-parser');
|
|
11
|
+
const { renderPersona } = require('../persona-renderer');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Transform agent data to Antigravity format.
|
|
15
|
+
* Produces a plain Markdown file (no YAML frontmatter) with three command
|
|
16
|
+
* sections: Quick Commands, Key Commands (exclusive), and All Commands.
|
|
17
|
+
*
|
|
18
|
+
* @param {object} agentData - Parsed agent data from agent-parser
|
|
19
|
+
* @param {object|null} agentData.agent - Parsed agent YAML block
|
|
20
|
+
* @param {object|null} agentData.persona_profile - Persona profile block
|
|
21
|
+
* @param {string} agentData.id - Agent ID (basename without .md)
|
|
22
|
+
* @param {string} agentData.filename - Source filename (e.g. sinapse-orqx.md)
|
|
23
|
+
* @param {Array} agentData.commands - Raw commands array
|
|
24
|
+
* @param {object} agentData.sections - Extracted markdown sections
|
|
25
|
+
* @returns {string} Transformed content ready to write to disk
|
|
26
|
+
*/
|
|
27
|
+
function transform(agentData) {
|
|
28
|
+
const agent = agentData.agent || {};
|
|
29
|
+
const persona = agentData.persona_profile || {};
|
|
30
|
+
|
|
31
|
+
const icon = agent.icon || '🤖';
|
|
32
|
+
const name = agent.name || agentData.id;
|
|
33
|
+
const title = agent.title || 'SINAPSE Agent';
|
|
34
|
+
const whenToUse = agent.whenToUse || 'Use this agent for specific tasks';
|
|
35
|
+
const archetype = persona.archetype || '';
|
|
36
|
+
|
|
37
|
+
// Normalize commands to a consistent {name, description, visibility} shape
|
|
38
|
+
const allCommands = normalizeCommands(agentData.commands || []);
|
|
39
|
+
const quickCommands = getVisibleCommands(allCommands, 'quick');
|
|
40
|
+
const keyCommands = getVisibleCommands(allCommands, 'key');
|
|
41
|
+
|
|
42
|
+
// Build header
|
|
43
|
+
let content = `# ${name} (@${agentData.id})
|
|
44
|
+
|
|
45
|
+
${icon} **${title}**${archetype ? ` | ${archetype}` : ''}
|
|
46
|
+
|
|
47
|
+
> ${whenToUse}
|
|
48
|
+
|
|
49
|
+
`;
|
|
50
|
+
|
|
51
|
+
// PARIDADE-IDE-002: include the persona contract, not just the label.
|
|
52
|
+
content += renderPersona(agentData, { includeCommunication: true });
|
|
53
|
+
|
|
54
|
+
// Quick Commands section
|
|
55
|
+
if (quickCommands.length > 0) {
|
|
56
|
+
content += `## Quick Commands
|
|
57
|
+
|
|
58
|
+
`;
|
|
59
|
+
for (const cmd of quickCommands) {
|
|
60
|
+
content += `- \`*${cmd.name}\` - ${cmd.description || 'No description'}\n`;
|
|
61
|
+
}
|
|
62
|
+
content += '\n';
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Key Commands section — only commands NOT already in Quick
|
|
66
|
+
const keyOnlyCommands = keyCommands.filter(
|
|
67
|
+
(k) => !quickCommands.some((q) => q.name === k.name)
|
|
68
|
+
);
|
|
69
|
+
if (keyOnlyCommands.length > 0) {
|
|
70
|
+
content += `## Key Commands
|
|
71
|
+
|
|
72
|
+
`;
|
|
73
|
+
for (const cmd of keyOnlyCommands) {
|
|
74
|
+
content += `- \`*${cmd.name}\` - ${cmd.description || 'No description'}\n`;
|
|
75
|
+
}
|
|
76
|
+
content += '\n';
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// All Commands section — shown when total exceeds what quick+key already covers
|
|
80
|
+
if (allCommands.length > quickCommands.length + keyOnlyCommands.length) {
|
|
81
|
+
content += `## All Commands
|
|
82
|
+
|
|
83
|
+
`;
|
|
84
|
+
for (const cmd of allCommands) {
|
|
85
|
+
content += `- \`*${cmd.name}\` - ${cmd.description || 'No description'}\n`;
|
|
86
|
+
}
|
|
87
|
+
content += '\n';
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Collaboration section (optional)
|
|
91
|
+
if (agentData.sections && agentData.sections.collaboration) {
|
|
92
|
+
content += `## Collaboration
|
|
93
|
+
|
|
94
|
+
${agentData.sections.collaboration}
|
|
95
|
+
|
|
96
|
+
`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
content += `---
|
|
100
|
+
*SINAPSE Agent - Synced from .sinapse-ai/development/agents/${agentData.filename}*
|
|
101
|
+
`;
|
|
102
|
+
|
|
103
|
+
return content;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Get the target filename for this agent.
|
|
108
|
+
* Antigravity uses the same filename as the source agent file.
|
|
109
|
+
*
|
|
110
|
+
* @param {object} agentData - Parsed agent data
|
|
111
|
+
* @returns {string} Target filename (e.g. sinapse-orqx.md)
|
|
112
|
+
*/
|
|
113
|
+
function getFilename(agentData) {
|
|
114
|
+
return agentData.filename;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
module.exports = {
|
|
118
|
+
transform,
|
|
119
|
+
getFilename,
|
|
120
|
+
format: 'cursor-style',
|
|
121
|
+
};
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cursor Transformer - Condensed rules format
|
|
3
|
+
* @story 5.1 - IDE Sync Expansion
|
|
4
|
+
*
|
|
5
|
+
* Format: Cursor MDC rule with frontmatter, icon, title, quick commands
|
|
6
|
+
* Target: .cursor/rules/agents/*.mdc
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const { getVisibleCommands, normalizeCommands } = require('../agent-parser');
|
|
10
|
+
const { renderPersona } = require('../persona-renderer');
|
|
11
|
+
|
|
12
|
+
function escapeFrontmatterString(value) {
|
|
13
|
+
return String(value || '')
|
|
14
|
+
.replace(/\r?\n/g, ' ')
|
|
15
|
+
.replace(/'/g, "''")
|
|
16
|
+
.trim();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function toMdcFilename(filename, fallbackId = 'agent') {
|
|
20
|
+
const baseName = String(filename || `${fallbackId}.md`).replace(/\.md$/i, '');
|
|
21
|
+
return `${baseName}.mdc`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Transform agent data to Cursor format
|
|
26
|
+
* @param {object} agentData - Parsed agent data from agent-parser
|
|
27
|
+
* @returns {string} - Transformed content
|
|
28
|
+
*/
|
|
29
|
+
function transform(agentData) {
|
|
30
|
+
const agent = agentData.agent || {};
|
|
31
|
+
const persona = agentData.persona_profile || {};
|
|
32
|
+
|
|
33
|
+
const icon = agent.icon || '🤖';
|
|
34
|
+
const name = agent.name || agentData.id;
|
|
35
|
+
const title = agent.title || 'SINAPSE Agent';
|
|
36
|
+
const whenToUse = agent.whenToUse || 'Use this agent for specific tasks';
|
|
37
|
+
const archetype = persona.archetype || '';
|
|
38
|
+
const description = escapeFrontmatterString(`SINAPSE agent @${agentData.id} - ${title}`);
|
|
39
|
+
|
|
40
|
+
// Get quick visibility commands (normalized to consistent format)
|
|
41
|
+
const allCommands = normalizeCommands(agentData.commands || []);
|
|
42
|
+
const quickCommands = getVisibleCommands(allCommands, 'quick');
|
|
43
|
+
const keyCommands = getVisibleCommands(allCommands, 'key');
|
|
44
|
+
|
|
45
|
+
// Build content
|
|
46
|
+
let content = `---
|
|
47
|
+
description: '${description}'
|
|
48
|
+
alwaysApply: false
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
# ${name} (@${agentData.id})
|
|
52
|
+
|
|
53
|
+
${icon} **${title}**${archetype ? ` | ${archetype}` : ''}
|
|
54
|
+
|
|
55
|
+
> ${whenToUse}
|
|
56
|
+
|
|
57
|
+
`;
|
|
58
|
+
|
|
59
|
+
// PARIDADE-IDE-002: emit the persona contract (role/identity/style/focus +
|
|
60
|
+
// core principles) so the IDE stub reasons like the agent, not just a label.
|
|
61
|
+
content += renderPersona(agentData, { includeCommunication: true });
|
|
62
|
+
|
|
63
|
+
// Add quick commands section
|
|
64
|
+
if (quickCommands.length > 0) {
|
|
65
|
+
content += `## Quick Commands
|
|
66
|
+
|
|
67
|
+
`;
|
|
68
|
+
for (const cmd of quickCommands) {
|
|
69
|
+
content += `- \`*${cmd.name}\` - ${cmd.description || 'No description'}\n`;
|
|
70
|
+
}
|
|
71
|
+
content += '\n';
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Add key commands if different from quick
|
|
75
|
+
const keyOnlyCommands = keyCommands.filter(
|
|
76
|
+
k => !quickCommands.some(q => q.name === k.name)
|
|
77
|
+
);
|
|
78
|
+
if (keyOnlyCommands.length > 0) {
|
|
79
|
+
content += `## Key Commands
|
|
80
|
+
|
|
81
|
+
`;
|
|
82
|
+
for (const cmd of keyOnlyCommands) {
|
|
83
|
+
content += `- \`*${cmd.name}\` - ${cmd.description || 'No description'}\n`;
|
|
84
|
+
}
|
|
85
|
+
content += '\n';
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Add collaboration section if available
|
|
89
|
+
if (agentData.sections && agentData.sections.collaboration) {
|
|
90
|
+
content += `## Collaboration
|
|
91
|
+
|
|
92
|
+
${agentData.sections.collaboration}
|
|
93
|
+
|
|
94
|
+
`;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
content += `---
|
|
98
|
+
*SINAPSE Agent - Synced from .sinapse-ai/development/agents/${agentData.filename}*
|
|
99
|
+
`;
|
|
100
|
+
|
|
101
|
+
return content;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Get the target filename for this agent
|
|
106
|
+
* @param {object} agentData - Parsed agent data
|
|
107
|
+
* @returns {string} - Target filename (.mdc extension)
|
|
108
|
+
*/
|
|
109
|
+
function getFilename(agentData) {
|
|
110
|
+
return toMdcFilename(agentData.filename, agentData.id);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
module.exports = {
|
|
114
|
+
transform,
|
|
115
|
+
getFilename,
|
|
116
|
+
toMdcFilename,
|
|
117
|
+
escapeFrontmatterString,
|
|
118
|
+
format: 'condensed-rules',
|
|
119
|
+
};
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Copilot Transformer - YAML frontmatter + condensed markdown
|
|
3
|
+
* @story 5.1 - IDE Sync Expansion
|
|
4
|
+
*
|
|
5
|
+
* Format: .agent.md files with YAML frontmatter (--- delimiters)
|
|
6
|
+
* Target: .github/agents/*.agent.md
|
|
7
|
+
*
|
|
8
|
+
* GitHub Copilot custom agents require:
|
|
9
|
+
* - YAML frontmatter with `description` (required), `name`, `tools`
|
|
10
|
+
* - Markdown body under 30,000 characters
|
|
11
|
+
* - File extension: .agent.md
|
|
12
|
+
*
|
|
13
|
+
* @see https://docs.github.com/en/copilot/reference/custom-agents-configuration
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const { normalizeCommands, getVisibleCommands } = require('../agent-parser');
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Transform agent data to GitHub Copilot custom agent format
|
|
20
|
+
* @param {object} agentData - Parsed agent data from agent-parser
|
|
21
|
+
* @returns {string} - Transformed content with YAML frontmatter
|
|
22
|
+
*/
|
|
23
|
+
function transform(agentData) {
|
|
24
|
+
const agent = agentData.agent || {};
|
|
25
|
+
const persona = agentData.persona_profile || {};
|
|
26
|
+
const yamlData = agentData.yaml || {};
|
|
27
|
+
const personaBlock = yamlData.persona || {};
|
|
28
|
+
|
|
29
|
+
const id = agent.id || agentData.id;
|
|
30
|
+
const name = agent.name || id;
|
|
31
|
+
const title = agent.title || 'SINAPSE Agent';
|
|
32
|
+
const icon = agent.icon || '';
|
|
33
|
+
const description = escapeYamlString(agent.whenToUse || `${title} agent for development tasks`);
|
|
34
|
+
|
|
35
|
+
// Build YAML frontmatter
|
|
36
|
+
const frontmatter = [
|
|
37
|
+
'---',
|
|
38
|
+
`name: ${id}`,
|
|
39
|
+
`description: '${description}'`,
|
|
40
|
+
`tools: ['read', 'edit', 'search', 'execute']`,
|
|
41
|
+
'---',
|
|
42
|
+
].join('\n');
|
|
43
|
+
|
|
44
|
+
// Build markdown body
|
|
45
|
+
const body = buildMarkdownBody({
|
|
46
|
+
id,
|
|
47
|
+
name,
|
|
48
|
+
title,
|
|
49
|
+
icon,
|
|
50
|
+
personaBlock,
|
|
51
|
+
persona,
|
|
52
|
+
commands: agentData.commands || [],
|
|
53
|
+
sections: agentData.sections || {},
|
|
54
|
+
corePrinciples: yamlData.core_principles,
|
|
55
|
+
filename: agentData.filename,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const content = `${frontmatter}\n\n${body}`;
|
|
59
|
+
|
|
60
|
+
// Enforce 30K character limit
|
|
61
|
+
if (content.length > 30000) {
|
|
62
|
+
return truncateContent(content, 30000);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return content;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Build the markdown body for the Copilot agent prompt
|
|
70
|
+
* @param {object} params - Agent parameters
|
|
71
|
+
* @returns {string} - Markdown body
|
|
72
|
+
*/
|
|
73
|
+
function buildMarkdownBody(params) {
|
|
74
|
+
const { id, name, title, icon, personaBlock, persona, commands, sections, filename } = params;
|
|
75
|
+
|
|
76
|
+
const parts = [];
|
|
77
|
+
|
|
78
|
+
// Header
|
|
79
|
+
const headerIcon = icon ? `${icon} ` : '';
|
|
80
|
+
parts.push(`# ${headerIcon}${name} Agent (@${id})\n`);
|
|
81
|
+
|
|
82
|
+
// Role description
|
|
83
|
+
if (personaBlock.role) {
|
|
84
|
+
parts.push(`You are an expert ${personaBlock.role}.\n`);
|
|
85
|
+
} else {
|
|
86
|
+
parts.push(`You are an expert ${title}.\n`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Identity + Focus (PARIDADE-IDE-002: keep the full persona contract)
|
|
90
|
+
if (personaBlock.identity) {
|
|
91
|
+
parts.push(`**Identity:** ${personaBlock.identity}\n`);
|
|
92
|
+
}
|
|
93
|
+
if (personaBlock.focus) {
|
|
94
|
+
parts.push(`**Focus:** ${personaBlock.focus}\n`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Style
|
|
98
|
+
if (personaBlock.style) {
|
|
99
|
+
parts.push(`## Style\n\n${personaBlock.style}\n`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Core principles (may be in persona block or at root level of YAML)
|
|
103
|
+
const corePrinciples = personaBlock.core_principles || params.corePrinciples;
|
|
104
|
+
if (corePrinciples && Array.isArray(corePrinciples)) {
|
|
105
|
+
parts.push('## Core Principles\n');
|
|
106
|
+
for (const principle of corePrinciples) {
|
|
107
|
+
// Handle both string and object formats (YAML may parse "KEY: value" as {KEY: value})
|
|
108
|
+
if (typeof principle === 'string') {
|
|
109
|
+
parts.push(`- ${principle}`);
|
|
110
|
+
} else if (typeof principle === 'object' && principle !== null) {
|
|
111
|
+
const entries = Object.entries(principle);
|
|
112
|
+
for (const [key, value] of entries) {
|
|
113
|
+
parts.push(`- ${key}: ${value}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
parts.push('');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Commands reference
|
|
121
|
+
const allCommands = normalizeCommands(commands);
|
|
122
|
+
const keyCommands = getVisibleCommands(allCommands, 'key');
|
|
123
|
+
const quickCommands = getVisibleCommands(allCommands, 'quick');
|
|
124
|
+
const displayCommands = keyCommands.length > 0 ? keyCommands : quickCommands.slice(0, 10);
|
|
125
|
+
|
|
126
|
+
if (displayCommands.length > 0) {
|
|
127
|
+
parts.push('## Commands\n');
|
|
128
|
+
parts.push('Use `*` prefix for commands:\n');
|
|
129
|
+
for (const cmd of displayCommands) {
|
|
130
|
+
parts.push(`- \`*${cmd.name}\` - ${cmd.description || 'No description'}`);
|
|
131
|
+
}
|
|
132
|
+
parts.push('');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Collaboration section (condensed)
|
|
136
|
+
if (sections.collaboration) {
|
|
137
|
+
parts.push(`## Collaboration\n\n${sections.collaboration}\n`);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Sync footer
|
|
141
|
+
parts.push('---');
|
|
142
|
+
parts.push(`*SINAPSE Agent - Synced from .sinapse-ai/development/agents/${filename}*`);
|
|
143
|
+
parts.push('');
|
|
144
|
+
|
|
145
|
+
return parts.join('\n');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Escape a string for use as a YAML single-quoted value
|
|
150
|
+
* Single quotes inside the string must be doubled
|
|
151
|
+
* @param {string} str - Input string
|
|
152
|
+
* @returns {string} - Escaped string
|
|
153
|
+
*/
|
|
154
|
+
function escapeYamlString(str) {
|
|
155
|
+
if (!str) return '';
|
|
156
|
+
// In YAML single-quoted strings, single quotes are escaped by doubling them
|
|
157
|
+
return str.replace(/'/g, "''");
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Truncate content to fit within character limit while keeping structure valid
|
|
162
|
+
* @param {string} content - Full content
|
|
163
|
+
* @param {number} maxChars - Maximum characters
|
|
164
|
+
* @returns {string} - Truncated content
|
|
165
|
+
*/
|
|
166
|
+
function truncateContent(content, maxChars) {
|
|
167
|
+
// Find the last complete section before the limit
|
|
168
|
+
const truncated = content.substring(0, maxChars - 100);
|
|
169
|
+
const lastNewline = truncated.lastIndexOf('\n\n');
|
|
170
|
+
|
|
171
|
+
if (lastNewline > 0) {
|
|
172
|
+
return truncated.substring(0, lastNewline) + '\n\n---\n*Content truncated to fit 30K limit*\n';
|
|
173
|
+
}
|
|
174
|
+
return truncated + '\n\n---\n*Content truncated to fit 30K limit*\n';
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Get the target filename for this agent (with .agent.md extension)
|
|
179
|
+
* @param {object} agentData - Parsed agent data
|
|
180
|
+
* @returns {string} - Target filename (e.g., "developer.agent.md")
|
|
181
|
+
*/
|
|
182
|
+
function getFilename(agentData) {
|
|
183
|
+
const id = (agentData.agent && agentData.agent.id) || agentData.id;
|
|
184
|
+
return `${id}.agent.md`;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
module.exports = {
|
|
188
|
+
transform,
|
|
189
|
+
getFilename,
|
|
190
|
+
format: 'github-copilot',
|
|
191
|
+
};
|