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,448 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kimi Transformer - Skill-based agent activator
|
|
3
|
+
* @story 5.1 - IDE Sync Expansion
|
|
4
|
+
*
|
|
5
|
+
* Format: SKILL.md with YAML frontmatter + markdown instructions
|
|
6
|
+
* Target: .kimi/skills/sinapse-{agent-id}/SKILL.md
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Transform agent data to Kimi skill format
|
|
11
|
+
* @param {object} agentData - Parsed agent data from agent-parser
|
|
12
|
+
* @returns {string} - Transformed SKILL.md content
|
|
13
|
+
*/
|
|
14
|
+
function transform(agentData) {
|
|
15
|
+
const rawAgent = agentData.agent || {};
|
|
16
|
+
const fallbackAgent = agentData._fallback || {};
|
|
17
|
+
const persona = agentData.persona_profile || {};
|
|
18
|
+
const comm = persona.communication || {};
|
|
19
|
+
const greetingLevels = comm.greeting_levels || {};
|
|
20
|
+
const yaml = agentData.yaml || {};
|
|
21
|
+
|
|
22
|
+
const id = agentData.id;
|
|
23
|
+
const skillId = getSkillId(agentData);
|
|
24
|
+
const activationId = getPreferredActivationId(agentData);
|
|
25
|
+
const name = rawAgent.name || fallbackAgent.name || id;
|
|
26
|
+
const title = rawAgent.title || fallbackAgent.title || 'SINAPSE Agent';
|
|
27
|
+
const icon = rawAgent.icon || fallbackAgent.icon || '🤖';
|
|
28
|
+
const whenToUse = rawAgent.whenToUse || fallbackAgent.whenToUse || `Use for ${title.toLowerCase()} tasks`;
|
|
29
|
+
const archetype = persona.archetype || rawAgent.archetype || 'Specialist';
|
|
30
|
+
|
|
31
|
+
const description = buildDescription(activationId, name, title, whenToUse);
|
|
32
|
+
const namedGreeting = greetingLevels.named || `${icon} ${name} ready`;
|
|
33
|
+
|
|
34
|
+
// Extract rich sections from parsed YAML
|
|
35
|
+
const identitySection = buildIdentitySection(rawAgent, persona, yaml);
|
|
36
|
+
const protocolSection = buildProtocolSection(yaml);
|
|
37
|
+
const commandsTable = buildCommandsTable(agentData.commands);
|
|
38
|
+
const workflowSection = buildWorkflowSection(yaml);
|
|
39
|
+
const guardrailsSection = buildGuardrailsSection(yaml);
|
|
40
|
+
const handoffsSection = buildHandoffsSection(yaml);
|
|
41
|
+
const outputContractSection = buildOutputContractSection(yaml);
|
|
42
|
+
|
|
43
|
+
// Full raw content
|
|
44
|
+
const rawContent = agentData.raw || '';
|
|
45
|
+
const rawContentSection = buildRawContentSection(rawContent, id);
|
|
46
|
+
|
|
47
|
+
const content = `---
|
|
48
|
+
name: ${JSON.stringify(skillId)}
|
|
49
|
+
description: ${JSON.stringify(description)}
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
# ${icon} @${id} — ${name}${archetype !== 'Specialist' ? ` (${archetype})` : ''} | ${title}
|
|
53
|
+
|
|
54
|
+
## Activation Protocol
|
|
55
|
+
|
|
56
|
+
When this skill is invoked:
|
|
57
|
+
|
|
58
|
+
1. Adopt the persona below immediately. Do NOT narrate the activation, do NOT comment on Kimi's mechanism, do NOT preface with internal reasoning.
|
|
59
|
+
2. Print the greeting verbatim from the next section.
|
|
60
|
+
3. List commands EXACTLY as they appear in the Star Commands table — do not summarize, do not invent shortcuts.
|
|
61
|
+
4. Wait for user input unless a star command was provided alongside the activation.
|
|
62
|
+
|
|
63
|
+
## Activation Greeting
|
|
64
|
+
|
|
65
|
+
\`\`\`text
|
|
66
|
+
${namedGreeting}
|
|
67
|
+
\`\`\`
|
|
68
|
+
|
|
69
|
+
${identitySection}${protocolSection}${commandsTable}${workflowSection}${guardrailsSection}${handoffsSection}${outputContractSection}---
|
|
70
|
+
|
|
71
|
+
## Full Agent Definition — ${id}
|
|
72
|
+
|
|
73
|
+
> This section contains the COMPLETE operating guide for this agent. Read it ENTIRELY and adopt the persona, principles, protocols, and guardrails defined below. Do NOT invent tasks, processes, or workflows that are not documented here.
|
|
74
|
+
|
|
75
|
+
${rawContentSection}`;
|
|
76
|
+
|
|
77
|
+
return content;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function buildIdentitySection(rawAgent, persona, yaml) {
|
|
81
|
+
const name = rawAgent.name || '';
|
|
82
|
+
const role = persona.role || yaml.persona?.role || '';
|
|
83
|
+
const style = persona.style || yaml.persona?.style || '';
|
|
84
|
+
const focus = persona.focus || yaml.persona?.focus || '';
|
|
85
|
+
const identity = persona.identity || yaml.persona?.identity || '';
|
|
86
|
+
|
|
87
|
+
if (!name && !role && !style && !focus && !identity) return '';
|
|
88
|
+
|
|
89
|
+
let section = '## Identity\n\n';
|
|
90
|
+
if (name) section += `- **Name:** ${name}\n`;
|
|
91
|
+
if (role) section += `- **Role:** ${role}\n`;
|
|
92
|
+
if (style) section += `- **Style:** ${style}\n`;
|
|
93
|
+
if (focus) section += `- **Focus:** ${focus}\n`;
|
|
94
|
+
if (identity) section += `- **Identity:** ${identity}\n`;
|
|
95
|
+
section += '\n';
|
|
96
|
+
return section;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function buildProtocolSection(yaml) {
|
|
100
|
+
const items = [];
|
|
101
|
+
|
|
102
|
+
if (yaml.cognitive_protocol && Array.isArray(yaml.cognitive_protocol)) {
|
|
103
|
+
items.push('### Cognitive Protocol\n\n' + yaml.cognitive_protocol.map(p => `- ${renderItem(p)}`).join('\n') + '\n');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (yaml.evidence_policy) {
|
|
107
|
+
const ep = yaml.evidence_policy;
|
|
108
|
+
let text = '### Evidence Policy\n\n';
|
|
109
|
+
if (ep.required_min_sources) text += `- Required minimum sources: ${ep.required_min_sources}\n`;
|
|
110
|
+
if (ep.accepted_types && Array.isArray(ep.accepted_types)) text += `- Accepted types: ${ep.accepted_types.join(', ')}\n`;
|
|
111
|
+
if (ep.reject_if && Array.isArray(ep.reject_if)) text += `- Reject if: ${ep.reject_if.map(r => `"${r}"`).join(', ')}\n`;
|
|
112
|
+
items.push(text + '\n');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (yaml.confidence_model) {
|
|
116
|
+
const cm = yaml.confidence_model;
|
|
117
|
+
let text = '### Confidence Model\n\n';
|
|
118
|
+
if (cm.formula) text += `- Formula: \`${cm.formula}\`\n`;
|
|
119
|
+
if (cm.thresholds) {
|
|
120
|
+
text += '- Thresholds:\n';
|
|
121
|
+
for (const [k, v] of Object.entries(cm.thresholds)) text += ` - ${k}: ${v}\n`;
|
|
122
|
+
}
|
|
123
|
+
items.push(text + '\n');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (yaml.core_principles && Array.isArray(yaml.core_principles)) {
|
|
127
|
+
items.push('### Core Principles\n\n' + yaml.core_principles.map(p => `- ${renderItem(p)}`).join('\n') + '\n');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return items.length > 0 ? items.join('\n') + '\n' : '';
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function buildCommandsTable(commands) {
|
|
134
|
+
const normalized = normalizeCommands(commands);
|
|
135
|
+
|
|
136
|
+
if (normalized.length === 0) {
|
|
137
|
+
return '';
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const allRows = normalized.map(cmd => {
|
|
141
|
+
let vis = 'full';
|
|
142
|
+
if (cmd.visibility) {
|
|
143
|
+
if (Array.isArray(cmd.visibility)) {
|
|
144
|
+
vis = cmd.visibility.join(', ');
|
|
145
|
+
} else if (typeof cmd.visibility === 'string') {
|
|
146
|
+
vis = cmd.visibility;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return `| \`*${cmd.name}\` | ${formatCommandDescription(cmd.description)} | ${vis} |`;
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// Always include guide/yolo/exit
|
|
153
|
+
const hasGuide = normalized.some(c => c.name === 'guide');
|
|
154
|
+
const hasYolo = normalized.some(c => c.name === 'yolo');
|
|
155
|
+
const hasExit = normalized.some(c => c.name === 'exit');
|
|
156
|
+
|
|
157
|
+
if (!hasGuide) allRows.push('| `*guide` | Show comprehensive usage guide | full |');
|
|
158
|
+
if (!hasYolo) allRows.push('| `*yolo` | Toggle permission mode | full |');
|
|
159
|
+
if (!hasExit) allRows.push('| `*exit` | Exit agent mode | full |');
|
|
160
|
+
|
|
161
|
+
return `## Star Commands\n\n| Command | Description | Visibility |\n|---------|-------------|------------|\n${allRows.join('\n')}\n\n`;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function normalizeCommands(commands) {
|
|
165
|
+
if (!commands) return [];
|
|
166
|
+
|
|
167
|
+
const commandList = Array.isArray(commands)
|
|
168
|
+
? commands
|
|
169
|
+
: (typeof commands === 'object'
|
|
170
|
+
? Object.entries(commands).map(([name, description]) => ({
|
|
171
|
+
name,
|
|
172
|
+
description,
|
|
173
|
+
visibility: ['full', 'quick'],
|
|
174
|
+
}))
|
|
175
|
+
: []);
|
|
176
|
+
|
|
177
|
+
return commandList
|
|
178
|
+
.map(normalizeCommand)
|
|
179
|
+
.filter(cmd => cmd && cmd.name);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function normalizeCommand(cmd) {
|
|
183
|
+
if (typeof cmd === 'string') {
|
|
184
|
+
const dashMatch = cmd.trim().match(/^\*?([a-zA-Z0-9:_-]+)\s*[-:]\s*(.+)$/);
|
|
185
|
+
if (dashMatch) {
|
|
186
|
+
return { name: dashMatch[1], description: dashMatch[2], visibility: ['full', 'quick'] };
|
|
187
|
+
}
|
|
188
|
+
return { name: cmd.replace(/^\*/, '') || 'unknown', description: 'No description', visibility: ['full', 'quick'] };
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (!cmd || typeof cmd !== 'object') return null;
|
|
192
|
+
if (cmd.name) return cmd;
|
|
193
|
+
|
|
194
|
+
const entries = Object.entries(cmd);
|
|
195
|
+
if (entries.length !== 1) return null;
|
|
196
|
+
|
|
197
|
+
const [name, value] = entries[0];
|
|
198
|
+
if (!name) return null;
|
|
199
|
+
|
|
200
|
+
if (value && typeof value === 'object') {
|
|
201
|
+
return {
|
|
202
|
+
name,
|
|
203
|
+
description: value.description || value.summary || 'No description',
|
|
204
|
+
visibility: value.visibility || ['full'],
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return {
|
|
209
|
+
name,
|
|
210
|
+
description: value === null || value === undefined ? 'No description' : String(value),
|
|
211
|
+
visibility: ['full'],
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function formatCommandDescription(value) {
|
|
216
|
+
if (value === null || value === undefined || value === '') return 'No description';
|
|
217
|
+
if (typeof value === 'string') return value;
|
|
218
|
+
return JSON.stringify(value);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function buildWorkflowSection(yaml) {
|
|
222
|
+
const items = [];
|
|
223
|
+
|
|
224
|
+
if (yaml.mandatory_flow && Array.isArray(yaml.mandatory_flow)) {
|
|
225
|
+
items.push('### Mandatory Flow\n\nExecute **strictly in this order**. No skips allowed.\n\n' +
|
|
226
|
+
yaml.mandatory_flow.map((step, i) => `${i + 1}. ${renderItem(step)}`).join('\n') + '\n');
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (yaml.phase_release_rules && Array.isArray(yaml.phase_release_rules)) {
|
|
230
|
+
items.push('### Phase Release Rules\n\n' + yaml.phase_release_rules.map(r => `- ${renderItem(r)}`).join('\n') + '\n');
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (yaml.activation_instructions && Array.isArray(yaml.activation_instructions)) {
|
|
234
|
+
items.push('### Activation Instructions\n\n' + yaml.activation_instructions.map(i => `- ${renderItem(i)}`).join('\n') + '\n');
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return items.length > 0 ? '## Workflow\n\n' + items.join('\n') + '\n' : '';
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function buildGuardrailsSection(yaml) {
|
|
241
|
+
const items = [];
|
|
242
|
+
|
|
243
|
+
if (yaml.veto_conditions && Array.isArray(yaml.veto_conditions)) {
|
|
244
|
+
items.push('### Veto Conditions\n\n' + yaml.veto_conditions.map(v => `- ❌ ${renderItem(v)}`).join('\n') + '\n');
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (yaml.agent_rules && Array.isArray(yaml.agent_rules)) {
|
|
248
|
+
items.push('### Agent Rules\n\n' + yaml.agent_rules.map(r => `- ${renderItem(r)}`).join('\n') + '\n');
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (yaml.design_rules) {
|
|
252
|
+
const dr = yaml.design_rules;
|
|
253
|
+
let text = '### Design Rules\n\n';
|
|
254
|
+
for (const [key, value] of Object.entries(dr)) {
|
|
255
|
+
if (value !== null && typeof value === 'object' && value.rule) {
|
|
256
|
+
text += `- **${key}:** ${value.rule}\n`;
|
|
257
|
+
} else if (typeof value === 'string') {
|
|
258
|
+
text += `- **${key}:** ${value}\n`;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
items.push(text + '\n');
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return items.length > 0 ? '## Guardrails\n\n' + items.join('\n') + '\n' : '';
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function buildHandoffsSection(yaml) {
|
|
268
|
+
if (!yaml.handoffs || !Array.isArray(yaml.handoffs) || yaml.handoffs.length === 0) {
|
|
269
|
+
return '';
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const rows = yaml.handoffs.map(h => {
|
|
273
|
+
const to = h.to || h.target || 'unknown';
|
|
274
|
+
const when = h.when || h.condition || '';
|
|
275
|
+
return `- **→ @${to}:** ${when}`;
|
|
276
|
+
}).join('\n');
|
|
277
|
+
|
|
278
|
+
return `## Handoffs\n\n${rows}\n\n`;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function buildOutputContractSection(yaml) {
|
|
282
|
+
if (!yaml.output_contract && !yaml.output) return '';
|
|
283
|
+
|
|
284
|
+
const oc = yaml.output_contract || yaml.output;
|
|
285
|
+
let text = '## Output Contract\n\n';
|
|
286
|
+
|
|
287
|
+
if (oc.required && Array.isArray(oc.required)) {
|
|
288
|
+
text += '**Required deliverables:**\n\n' + oc.required.map(r => `- ${r}`).join('\n') + '\n\n';
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (oc.done_when && Array.isArray(oc.done_when)) {
|
|
292
|
+
text += '**Done when:**\n\n' + oc.done_when.map(d => `- ✅ ${d}`).join('\n') + '\n\n';
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
return text;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function buildDescription(id, name, title, whenToUse) {
|
|
299
|
+
let desc = whenToUse
|
|
300
|
+
.replace(/\n/g, ' ')
|
|
301
|
+
.replace(/\s+/g, ' ')
|
|
302
|
+
.trim();
|
|
303
|
+
|
|
304
|
+
if (desc.length > 300) {
|
|
305
|
+
desc = desc.substring(0, 297) + '...';
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const triggerPhrases = [
|
|
309
|
+
`activate ${id}`,
|
|
310
|
+
`switch to ${id}`,
|
|
311
|
+
`@${id}`,
|
|
312
|
+
];
|
|
313
|
+
|
|
314
|
+
const brandedTitle = /^SINAPSE\b/i.test(title) ? title : `SINAPSE ${title}`;
|
|
315
|
+
return `Activate the ${brandedTitle} agent (${name}). ${desc} Trigger when user asks to ${id}, or says '${triggerPhrases.join("', '")}'.`;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function buildRawContentSection(rawContent, id) {
|
|
319
|
+
if (!rawContent || rawContent.length === 0) {
|
|
320
|
+
return '> Agent definition not available.\n';
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return sanitizeGeneratedMarkdown(rawContent);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function sanitizeGeneratedMarkdown(content) {
|
|
327
|
+
return addLanguageToUntypedFences(content)
|
|
328
|
+
.split('\n')
|
|
329
|
+
.map(sanitizeBareStarCommands)
|
|
330
|
+
.join('\n');
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function sanitizeBareStarCommands(line) {
|
|
334
|
+
return line
|
|
335
|
+
.split(/(`[^`]*`)/g)
|
|
336
|
+
.map(segment => {
|
|
337
|
+
if (segment.startsWith('`') && segment.endsWith('`')) {
|
|
338
|
+
return segment;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return segment.replace(
|
|
342
|
+
/(^|[\s(,])\*([a-zA-Z0-9][a-zA-Z0-9:_-]*)(?=([\s,).;:]|$))/g,
|
|
343
|
+
'$1`*$2`'
|
|
344
|
+
);
|
|
345
|
+
})
|
|
346
|
+
.join('');
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function addLanguageToUntypedFences(content) {
|
|
350
|
+
let inFence = false;
|
|
351
|
+
|
|
352
|
+
return content.split('\n').map((line) => {
|
|
353
|
+
if (/^```[ \t]*$/.test(line)) {
|
|
354
|
+
if (!inFence) {
|
|
355
|
+
inFence = true;
|
|
356
|
+
return '```text';
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
inFence = false;
|
|
360
|
+
return '```';
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
if (/^```/.test(line)) {
|
|
364
|
+
inFence = !inFence;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
return line;
|
|
368
|
+
}).join('\n');
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Normalize an array item that may be a string OR a YAML-parsed object.
|
|
372
|
+
// `- CRITICAL: text` parses to { CRITICAL: 'text' } and must render as
|
|
373
|
+
// `**CRITICAL:** text`, not `[object Object]`.
|
|
374
|
+
function renderItem(value) {
|
|
375
|
+
if (value === null || value === undefined) return '';
|
|
376
|
+
if (typeof value === 'string') return value;
|
|
377
|
+
if (typeof value !== 'object') return String(value);
|
|
378
|
+
|
|
379
|
+
const entries = Object.entries(value);
|
|
380
|
+
if (entries.length === 0) return '';
|
|
381
|
+
|
|
382
|
+
return entries
|
|
383
|
+
.map(([k, v]) => {
|
|
384
|
+
const rendered = typeof v === 'string' ? v : JSON.stringify(v);
|
|
385
|
+
return `**${k}:** ${rendered}`;
|
|
386
|
+
})
|
|
387
|
+
.join(' ');
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function getPreferredActivationId(agentData) {
|
|
391
|
+
const agent = agentData.agent || {};
|
|
392
|
+
const preferred = agent.preferredActivationAlias || agent.preferred_activation_alias;
|
|
393
|
+
return sanitizeSkillToken(preferred || agentData.id || 'agent');
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Returns the Kimi skill directory name for the given agent.
|
|
398
|
+
* Guarantees the prefix `sinapse-` on all agent IDs.
|
|
399
|
+
* @param {object} agentData - Parsed agent data from agent-parser
|
|
400
|
+
* @returns {string} - Skill directory name, e.g. "sinapse-developer"
|
|
401
|
+
*/
|
|
402
|
+
function getSkillId(agentData) {
|
|
403
|
+
const id = getPreferredActivationId(agentData);
|
|
404
|
+
if (id.startsWith('sinapse-')) return id;
|
|
405
|
+
return `sinapse-${id}`;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Returns the subdirectory name for the Kimi skill (equals getSkillId).
|
|
410
|
+
* @param {object} agentData - Parsed agent data from agent-parser
|
|
411
|
+
* @returns {string} - Directory name
|
|
412
|
+
*/
|
|
413
|
+
function getDirname(agentData) {
|
|
414
|
+
return getSkillId(agentData);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Returns the filename for the Kimi skill file.
|
|
419
|
+
* Always "SKILL.md" — the directory is the differentiator.
|
|
420
|
+
* @returns {string}
|
|
421
|
+
*/
|
|
422
|
+
function getFilename(_agentData) {
|
|
423
|
+
return 'SKILL.md';
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function sanitizeSkillToken(value) {
|
|
427
|
+
const raw = String(value || '')
|
|
428
|
+
.trim()
|
|
429
|
+
.replace(/^@+/, '');
|
|
430
|
+
const normalized = raw.normalize('NFKD').replace(/[̀-ͯ]/g, '');
|
|
431
|
+
const safe = normalized
|
|
432
|
+
.replace(/[\\/]+/g, '-')
|
|
433
|
+
.replace(/\.\.+/g, '-')
|
|
434
|
+
.replace(/[^a-zA-Z0-9_-]+/g, '-')
|
|
435
|
+
.replace(/-+/g, '-')
|
|
436
|
+
.replace(/^[-_]+|[-_]+$/g, '')
|
|
437
|
+
.toLowerCase();
|
|
438
|
+
|
|
439
|
+
return safe || 'agent';
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
module.exports = {
|
|
443
|
+
transform,
|
|
444
|
+
getSkillId,
|
|
445
|
+
getDirname,
|
|
446
|
+
getFilename,
|
|
447
|
+
format: 'kimi-skill',
|
|
448
|
+
};
|