@modus-ai/modus 0.1.5 → 0.1.7
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/dist/cli/index.js +28 -10
- package/dist/cli/index.js.map +1 -1
- package/dist/commands/init.d.ts +6 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +183 -216
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +15 -6
- package/dist/commands/update.js.map +1 -1
- package/dist/generators/claude.d.ts +9 -0
- package/dist/generators/claude.d.ts.map +1 -0
- package/dist/generators/claude.js +256 -0
- package/dist/generators/claude.js.map +1 -0
- package/dist/generators/copilot.d.ts +9 -0
- package/dist/generators/copilot.d.ts.map +1 -0
- package/dist/generators/copilot.js +154 -0
- package/dist/generators/copilot.js.map +1 -0
- package/dist/generators/cursor.d.ts +9 -0
- package/dist/generators/cursor.d.ts.map +1 -0
- package/dist/generators/cursor.js +220 -0
- package/dist/generators/cursor.js.map +1 -0
- package/dist/generators/index.d.ts +9 -0
- package/dist/generators/index.d.ts.map +1 -0
- package/dist/generators/index.js +26 -0
- package/dist/generators/index.js.map +1 -0
- package/dist/utils/config.d.ts +15 -0
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js.map +1 -1
- package/package.json +1 -1
- package/templates/hooks/session-start.py +51 -1
- package/templates/skills/modus-init/SKILL.md +75 -0
- package/templates/skills/modus-plan/SKILL.md +22 -0
- package/templates/skills/modus-spec/SKILL.md +22 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ModusConfig } from '../utils/config.js';
|
|
2
|
+
import type { GenerateOptions } from './codebuddy.js';
|
|
3
|
+
export declare function generateCursorFiles(projectRoot: string, config: ModusConfig, _opts?: GenerateOptions): void;
|
|
4
|
+
/**
|
|
5
|
+
* Sync a single business Skill to Cursor format.
|
|
6
|
+
* Creates a rule at .cursor/rules/modus-biz-{domain}.mdc
|
|
7
|
+
*/
|
|
8
|
+
export declare function syncBizSkillToCursor(projectRoot: string, domain: string, skillContent: string): void;
|
|
9
|
+
//# sourceMappingURL=cursor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor.d.ts","sourceRoot":"","sources":["../../src/generators/cursor.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AA0DtD,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,WAAW,EACnB,KAAK,GAAE,eAAoB,GAC1B,IAAI,CAMN;AAgLD;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,GACnB,IAAI,CAON"}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { writeFile, readTemplate, fileExists, readFile } from '../utils/file-system.js';
|
|
3
|
+
const RULES_BASE = path.join('.cursor', 'rules');
|
|
4
|
+
const MCP_PATH = path.join('.cursor', 'mcp.json');
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// Framework Skills → individual .mdc rules
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
const SKILL_RULES = [
|
|
9
|
+
{
|
|
10
|
+
id: 'modus-init',
|
|
11
|
+
templatePath: 'skills/modus-init/SKILL.md',
|
|
12
|
+
description: 'Run /modus:init to scan the codebase and build Business Skills for each domain',
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
id: 'modus-vibe',
|
|
16
|
+
templatePath: 'skills/modus-vibe/SKILL.md',
|
|
17
|
+
description: 'Run /modus:vibe to start context-aware vibe coding with business Skill preloading',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
id: 'modus-plan',
|
|
21
|
+
templatePath: 'skills/modus-plan/SKILL.md',
|
|
22
|
+
description: 'Run /modus:plan to create structured feature proposals with complexity grading',
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: 'modus-spec',
|
|
26
|
+
templatePath: 'skills/modus-spec/SKILL.md',
|
|
27
|
+
description: 'Run /modus:spec for OpenSpec-driven development with delta specs and GIVEN/WHEN/THEN scenarios',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: 'modus-auto',
|
|
31
|
+
templatePath: 'skills/modus-auto/SKILL.md',
|
|
32
|
+
description: 'Run /modus:auto with a TAPD story URL to get AI-recommended mode (vibe/plan/spec/harness)',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
id: 'modus-harness',
|
|
36
|
+
templatePath: 'skills/modus-harness/SKILL.md',
|
|
37
|
+
description: 'Run /modus:harness to execute the full dual-loop multi-agent development pipeline',
|
|
38
|
+
},
|
|
39
|
+
];
|
|
40
|
+
// Harness sub-agent templates to merge into the single harness rule
|
|
41
|
+
const HARNESS_SUB_AGENT_TEMPLATES = [
|
|
42
|
+
{ id: '00-skills-builder', templatePath: 'skills/modus-harness-agents/00-skills-builder/SKILL.md' },
|
|
43
|
+
{ id: '01-analysis', templatePath: 'skills/modus-harness-agents/01-analysis/SKILL.md' },
|
|
44
|
+
{ id: '02-dev', templatePath: 'skills/modus-harness-agents/02-dev/SKILL.md' },
|
|
45
|
+
{ id: '03-test', templatePath: 'skills/modus-harness-agents/03-test/SKILL.md' },
|
|
46
|
+
{ id: '04-perf', templatePath: 'skills/modus-harness-agents/04-perf/SKILL.md' },
|
|
47
|
+
{ id: '05-security', templatePath: 'skills/modus-harness-agents/05-security/SKILL.md' },
|
|
48
|
+
{ id: '06-review', templatePath: 'skills/modus-harness-agents/06-review/SKILL.md' },
|
|
49
|
+
{ id: '07-deploy', templatePath: 'skills/modus-harness-agents/07-deploy/SKILL.md' },
|
|
50
|
+
];
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
// Main entry
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
export function generateCursorFiles(projectRoot, config, _opts = {}) {
|
|
55
|
+
generateCursorSkillRules(projectRoot, config);
|
|
56
|
+
generateCursorHarnessRule(projectRoot);
|
|
57
|
+
generateCursorConstitution(projectRoot, config);
|
|
58
|
+
generateCursorAutoVibe(projectRoot);
|
|
59
|
+
generateCursorMcp(projectRoot, config);
|
|
60
|
+
}
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
// Individual skill rules
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
function generateCursorSkillRules(projectRoot, config) {
|
|
65
|
+
const enabled = new Set(config.commands.enabled);
|
|
66
|
+
for (const skill of SKILL_RULES) {
|
|
67
|
+
// Only generate command-backed skills if the command is enabled
|
|
68
|
+
const cmdId = skill.id.replace('modus-', '');
|
|
69
|
+
if (['init', 'vibe', 'plan', 'spec', 'auto', 'harness'].includes(cmdId) && !enabled.has(cmdId)) {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
let body;
|
|
73
|
+
try {
|
|
74
|
+
body = readTemplate(skill.templatePath);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
const content = buildMdcFile(skill.description, false, body);
|
|
80
|
+
writeFile(path.join(projectRoot, RULES_BASE, `${skill.id}.mdc`), content);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
// Harness rule — single-agent degradation
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
function generateCursorHarnessRule(projectRoot) {
|
|
87
|
+
const sections = [
|
|
88
|
+
'# Modus Harness — Single-Agent Mode',
|
|
89
|
+
'',
|
|
90
|
+
'> **Note:** Cursor does not support native Sub-Agents. The Harness pipeline runs in',
|
|
91
|
+
'> single-agent mode: the orchestrator executes each step sequentially within one context.',
|
|
92
|
+
'> For full parallel Sub-Agent support, use CodeBuddy or Claude Code.',
|
|
93
|
+
'',
|
|
94
|
+
'## Pipeline Steps (sequential)',
|
|
95
|
+
'',
|
|
96
|
+
];
|
|
97
|
+
for (const agent of HARNESS_SUB_AGENT_TEMPLATES) {
|
|
98
|
+
let body;
|
|
99
|
+
try {
|
|
100
|
+
body = readTemplate(agent.templatePath);
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
sections.push(`### Step ${agent.id}`, '', body, '', '---', '');
|
|
106
|
+
}
|
|
107
|
+
const content = buildMdcFile('Modus Harness orchestration steps (single-agent mode for Cursor)', false, sections.join('\n'));
|
|
108
|
+
writeFile(path.join(projectRoot, RULES_BASE, 'modus-harness-agents.mdc'), content);
|
|
109
|
+
}
|
|
110
|
+
// ---------------------------------------------------------------------------
|
|
111
|
+
// Constitution rule
|
|
112
|
+
// ---------------------------------------------------------------------------
|
|
113
|
+
function generateCursorConstitution(projectRoot, config) {
|
|
114
|
+
const any = config;
|
|
115
|
+
const constitution = any['constitution'] ?? {};
|
|
116
|
+
const techStack = String(constitution['tech_stack'] ?? config.techStack ?? '');
|
|
117
|
+
const buildCmd = String(constitution['build_command'] ?? '');
|
|
118
|
+
const testCmd = String(constitution['test_command'] ?? '');
|
|
119
|
+
const context = config.context ?? '';
|
|
120
|
+
const hardRules = Array.isArray(constitution['hard_rules'])
|
|
121
|
+
? constitution['hard_rules'].map((r) => `- ${r}`).join('\n')
|
|
122
|
+
: '';
|
|
123
|
+
const keyPatterns = Array.isArray(constitution['key_patterns'])
|
|
124
|
+
? constitution['key_patterns'].map((p) => `- ${p}`).join('\n')
|
|
125
|
+
: '';
|
|
126
|
+
const lines = ['# Modus Project Constitution', ''];
|
|
127
|
+
if (context) {
|
|
128
|
+
lines.push(`> ${context}`, '');
|
|
129
|
+
}
|
|
130
|
+
if (techStack) {
|
|
131
|
+
lines.push(`**Tech Stack:** ${techStack}`, '');
|
|
132
|
+
}
|
|
133
|
+
if (buildCmd) {
|
|
134
|
+
lines.push(`**Build Command:** \`${buildCmd}\``, '');
|
|
135
|
+
}
|
|
136
|
+
if (testCmd) {
|
|
137
|
+
lines.push(`**Test Command:** \`${testCmd}\``, '');
|
|
138
|
+
}
|
|
139
|
+
if (hardRules) {
|
|
140
|
+
lines.push('**Hard Rules (must never violate):**', hardRules, '');
|
|
141
|
+
}
|
|
142
|
+
if (keyPatterns) {
|
|
143
|
+
lines.push('**Key Patterns:**', keyPatterns, '');
|
|
144
|
+
}
|
|
145
|
+
lines.push('**Modus Commands:** `/modus:init` · `/modus:vibe` · `/modus:plan` · `/modus:spec` · `/modus:auto` · `/modus:harness`');
|
|
146
|
+
const content = buildMdcFile('Modus project constitution — tech stack, hard rules, and key patterns', true, lines.join('\n'));
|
|
147
|
+
writeFile(path.join(projectRoot, RULES_BASE, 'modus-constitution.mdc'), content);
|
|
148
|
+
}
|
|
149
|
+
// ---------------------------------------------------------------------------
|
|
150
|
+
// Auto-vibe — always-on rule that activates vibe behavior automatically
|
|
151
|
+
// ---------------------------------------------------------------------------
|
|
152
|
+
function generateCursorAutoVibe(projectRoot) {
|
|
153
|
+
const body = [
|
|
154
|
+
'# Modus Auto-Vibe Mode',
|
|
155
|
+
'',
|
|
156
|
+
'This project has Modus initialized. **For every conversation, automatically apply',
|
|
157
|
+
'vibe-mode behavior** — do not wait for the user to type `/modus:vibe`.',
|
|
158
|
+
'',
|
|
159
|
+
'## Auto-Vibe Instructions',
|
|
160
|
+
'',
|
|
161
|
+
'1. At the start of each conversation, read `modus/knowledge-catalog.md` to understand',
|
|
162
|
+
' available Business Skills and their domains.',
|
|
163
|
+
'2. Based on the user\'s request, identify the relevant business domain(s).',
|
|
164
|
+
'3. Load the corresponding `.cursor/rules/modus-biz-{domain}.mdc` rule(s) to get',
|
|
165
|
+
' business context (entities, rules, pitfalls, key files).',
|
|
166
|
+
'4. Respond as a developer who deeply understands this project — apply the',
|
|
167
|
+
' business rules, naming conventions, and known pitfalls from the loaded Skills.',
|
|
168
|
+
'5. If the user explicitly triggers `/modus:plan`, `/modus:spec`, or `/modus:harness`,',
|
|
169
|
+
' switch to that mode\'s workflow.',
|
|
170
|
+
'',
|
|
171
|
+
'## Token Efficiency',
|
|
172
|
+
'',
|
|
173
|
+
'- Load Skills **on demand** (only for domains relevant to the current request).',
|
|
174
|
+
'- Skip loading if the request is clearly out of project scope (e.g. general questions).',
|
|
175
|
+
'- Re-use already-loaded Skill context within the same conversation.',
|
|
176
|
+
].join('\n');
|
|
177
|
+
const content = buildMdcFile('Auto-apply Modus vibe mode for every conversation — load Business Skills based on user request', true, body);
|
|
178
|
+
writeFile(path.join(projectRoot, RULES_BASE, 'modus-auto-vibe.mdc'), content);
|
|
179
|
+
}
|
|
180
|
+
// ---------------------------------------------------------------------------
|
|
181
|
+
// MCP config — .cursor/mcp.json
|
|
182
|
+
// ---------------------------------------------------------------------------
|
|
183
|
+
function generateCursorMcp(projectRoot, config) {
|
|
184
|
+
if (!config.mcpServers || Object.keys(config.mcpServers).length === 0)
|
|
185
|
+
return;
|
|
186
|
+
const mcpPath = path.join(projectRoot, MCP_PATH);
|
|
187
|
+
let existing = {};
|
|
188
|
+
if (fileExists(mcpPath)) {
|
|
189
|
+
try {
|
|
190
|
+
existing = JSON.parse(readFile(mcpPath));
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
existing = {};
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
const existingServers = existing['mcpServers'] ?? {};
|
|
197
|
+
existing['mcpServers'] = { ...config.mcpServers, ...existingServers };
|
|
198
|
+
writeFile(mcpPath, JSON.stringify(existing, null, 2) + '\n');
|
|
199
|
+
}
|
|
200
|
+
// ---------------------------------------------------------------------------
|
|
201
|
+
// Helpers
|
|
202
|
+
// ---------------------------------------------------------------------------
|
|
203
|
+
/**
|
|
204
|
+
* Wrap markdown body in a Cursor .mdc frontmatter header.
|
|
205
|
+
*/
|
|
206
|
+
function buildMdcFile(description, alwaysApply, body) {
|
|
207
|
+
return `---\ndescription: "${description.replace(/"/g, '\\"')}"\nalwaysApply: ${alwaysApply}\n---\n\n${body}\n`;
|
|
208
|
+
}
|
|
209
|
+
// ---------------------------------------------------------------------------
|
|
210
|
+
// Business Skill sync
|
|
211
|
+
// ---------------------------------------------------------------------------
|
|
212
|
+
/**
|
|
213
|
+
* Sync a single business Skill to Cursor format.
|
|
214
|
+
* Creates a rule at .cursor/rules/modus-biz-{domain}.mdc
|
|
215
|
+
*/
|
|
216
|
+
export function syncBizSkillToCursor(projectRoot, domain, skillContent) {
|
|
217
|
+
const content = buildMdcFile(`Business rules and conventions for the ${domain} domain`, false, skillContent);
|
|
218
|
+
writeFile(path.join(projectRoot, RULES_BASE, `modus-biz-${domain}.mdc`), content);
|
|
219
|
+
}
|
|
220
|
+
//# sourceMappingURL=cursor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../src/generators/cursor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type GenerateOptions } from './codebuddy.js';
|
|
2
|
+
import type { ModusConfig } from '../utils/config.js';
|
|
3
|
+
export type { GenerateOptions };
|
|
4
|
+
/**
|
|
5
|
+
* Generate configuration files for every platform listed in `config.platforms`.
|
|
6
|
+
* Defaults to ['codebuddy'] when the field is absent to preserve backward compat.
|
|
7
|
+
*/
|
|
8
|
+
export declare function generateAllPlatformFiles(projectRoot: string, config: ModusConfig, opts?: GenerateOptions): void;
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/generators/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAA0B,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAI9E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEtD,YAAY,EAAE,eAAe,EAAE,CAAC;AAEhC;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,WAAW,EACnB,IAAI,GAAE,eAAoB,GACzB,IAAI,CAeN"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Platform dispatcher — generates configuration files for all selected AI platforms.
|
|
2
|
+
// Called by `modus init` and `modus update`; replaces direct calls to generateCodeBuddyFiles.
|
|
3
|
+
import { generateCodeBuddyFiles } from './codebuddy.js';
|
|
4
|
+
import { generateClaudeFiles } from './claude.js';
|
|
5
|
+
import { generateCursorFiles } from './cursor.js';
|
|
6
|
+
import { generateCopilotFiles } from './copilot.js';
|
|
7
|
+
/**
|
|
8
|
+
* Generate configuration files for every platform listed in `config.platforms`.
|
|
9
|
+
* Defaults to ['codebuddy'] when the field is absent to preserve backward compat.
|
|
10
|
+
*/
|
|
11
|
+
export function generateAllPlatformFiles(projectRoot, config, opts = {}) {
|
|
12
|
+
const platforms = config.platforms ?? ['codebuddy'];
|
|
13
|
+
if (platforms.includes('codebuddy')) {
|
|
14
|
+
generateCodeBuddyFiles(projectRoot, config, opts);
|
|
15
|
+
}
|
|
16
|
+
if (platforms.includes('claude')) {
|
|
17
|
+
generateClaudeFiles(projectRoot, config, opts);
|
|
18
|
+
}
|
|
19
|
+
if (platforms.includes('cursor')) {
|
|
20
|
+
generateCursorFiles(projectRoot, config, opts);
|
|
21
|
+
}
|
|
22
|
+
if (platforms.includes('copilot')) {
|
|
23
|
+
generateCopilotFiles(projectRoot, config, opts);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/generators/index.ts"],"names":[],"mappings":"AAAA,qFAAqF;AACrF,8FAA8F;AAC9F,OAAO,EAAE,sBAAsB,EAAwB,MAAM,gBAAgB,CAAC;AAC9E,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAKpD;;;GAGG;AACH,MAAM,UAAU,wBAAwB,CACtC,WAAmB,EACnB,MAAmB,EACnB,OAAwB,EAAE;IAE1B,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,IAAI,CAAC,WAAW,CAAC,CAAC;IAEpD,IAAI,SAAS,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;QACpC,sBAAsB,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;IACpD,CAAC;IACD,IAAI,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QACjC,mBAAmB,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;IACjD,CAAC;IACD,IAAI,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QACjC,mBAAmB,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;IACjD,CAAC;IACD,IAAI,SAAS,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QAClC,oBAAoB,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;IAClD,CAAC;AACH,CAAC"}
|
package/dist/utils/config.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/** Supported AI platform targets for file generation. */
|
|
2
|
+
export type PlatformId = 'codebuddy' | 'claude' | 'cursor' | 'copilot';
|
|
1
3
|
export interface ModusAnalyticsConfig {
|
|
2
4
|
/** Whether to send usage events. Defaults to true. */
|
|
3
5
|
enabled?: boolean;
|
|
@@ -23,6 +25,19 @@ export interface ModusConfig {
|
|
|
23
25
|
mcpServers?: Record<string, unknown>;
|
|
24
26
|
/** Usage analytics — sends command/subagent events to the Collector API. */
|
|
25
27
|
analytics?: ModusAnalyticsConfig;
|
|
28
|
+
/**
|
|
29
|
+
* AI platforms to generate configuration files for.
|
|
30
|
+
* Set during `modus init`; refreshed by `modus update`.
|
|
31
|
+
* Defaults to ['codebuddy'] when omitted.
|
|
32
|
+
*/
|
|
33
|
+
platforms?: PlatformId[];
|
|
34
|
+
/**
|
|
35
|
+
* The modus CLI version that last wrote the platform files.
|
|
36
|
+
* Written by `modus init` and `modus update`.
|
|
37
|
+
* The session-start hook compares this against the currently installed version
|
|
38
|
+
* and auto-runs `modus update` when they differ (silent auto-update).
|
|
39
|
+
*/
|
|
40
|
+
modusBuildVersion?: string;
|
|
26
41
|
}
|
|
27
42
|
export declare function getConfigPath(projectRoot: string): string;
|
|
28
43
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,oBAAoB;IACnC,sDAAsD;IACtD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,oEAAoE;IACpE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qDAAqD;IACrD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE;QACR,OAAO,EAAE,MAAM,EAAE,CAAC;KACnB,CAAC;IACF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6FAA6F;IAC7F,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,4EAA4E;IAC5E,SAAS,CAAC,EAAE,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAKA,yDAAyD;AACzD,MAAM,MAAM,UAAU,GAAG,WAAW,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEvE,MAAM,WAAW,oBAAoB;IACnC,sDAAsD;IACtD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,oEAAoE;IACpE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qDAAqD;IACrD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE;QACR,OAAO,EAAE,MAAM,EAAE,CAAC;KACnB,CAAC;IACF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6FAA6F;IAC7F,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,4EAA4E;IAC5E,SAAS,CAAC,EAAE,oBAAoB,CAAC;IACjC;;;;OAIG;IACH,SAAS,CAAC,EAAE,UAAU,EAAE,CAAC;IACzB;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AASD,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED;;;GAGG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,wBAAgB,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,WAAW,CAK3D;AAED,wBAAgB,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI,CAIzE"}
|
package/dist/utils/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,SAAS,CAAC;AAC3B,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,SAAS,CAAC;AAC3B,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AA8CnE,MAAM,cAAc,GAAgB;IAClC,OAAO,EAAE,GAAG;IACZ,QAAQ,EAAE;QACR,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC;KACrD;CACF,CAAC;AAEF,MAAM,UAAU,aAAa,CAAC,WAAmB;IAC/C,OAAO,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,OAAO,EAAE,aAAa,CAAC,CAAC;AACxD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY;IAC1B,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,YAAY,EAAE,QAAQ,CAAC,CAAC;AACzD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,QAAgB;IACzC,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;AACjE,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,WAAmB;IAC5C,MAAM,UAAU,GAAG,aAAa,CAAC,WAAW,CAAC,CAAC;IAC9C,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC;QAAE,OAAO,EAAE,GAAG,cAAc,EAAE,CAAC;IAC1D,MAAM,GAAG,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC;IACjC,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAgB,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,WAAmB,EAAE,MAAmB;IACjE,MAAM,UAAU,GAAG,aAAa,CAAC,WAAW,CAAC,CAAC;IAC9C,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;IACtD,SAAS,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;AACjC,CAAC"}
|
package/package.json
CHANGED
|
@@ -65,12 +65,54 @@ def read_file_safe(path: str, max_bytes: int = 8000) -> str:
|
|
|
65
65
|
return ""
|
|
66
66
|
|
|
67
67
|
|
|
68
|
+
def _silent_auto_update(project_dir: str) -> bool:
|
|
69
|
+
"""
|
|
70
|
+
Compare the modus CLI version that generated the current project files
|
|
71
|
+
(stored in modus/config.yaml modusBuildVersion) with the currently installed
|
|
72
|
+
modus version. If they differ, silently re-run `modus update` so users get
|
|
73
|
+
new templates automatically after an npm upgrade — no manual action needed.
|
|
74
|
+
|
|
75
|
+
Returns True if an update was performed.
|
|
76
|
+
"""
|
|
77
|
+
try:
|
|
78
|
+
import subprocess
|
|
79
|
+
import yaml # type: ignore[import-untyped]
|
|
80
|
+
|
|
81
|
+
config_path = os.path.join(project_dir, "modus", "config.yaml")
|
|
82
|
+
with open(config_path, encoding="utf-8") as f:
|
|
83
|
+
config = yaml.safe_load(f) or {}
|
|
84
|
+
|
|
85
|
+
stored_version = (config.get("modusBuildVersion") or "").strip()
|
|
86
|
+
if not stored_version:
|
|
87
|
+
return False # Never stamped — skip (pre-feature install)
|
|
88
|
+
|
|
89
|
+
result = subprocess.run(
|
|
90
|
+
["modus", "--version"],
|
|
91
|
+
capture_output=True, text=True, timeout=5,
|
|
92
|
+
)
|
|
93
|
+
current_version = result.stdout.strip()
|
|
94
|
+
if not current_version or current_version == stored_version:
|
|
95
|
+
return False # Up to date
|
|
96
|
+
|
|
97
|
+
# Versions differ — silently regenerate all platform files
|
|
98
|
+
subprocess.run(
|
|
99
|
+
["modus", "update", "--root", project_dir],
|
|
100
|
+
capture_output=True, timeout=60,
|
|
101
|
+
)
|
|
102
|
+
return True
|
|
103
|
+
except Exception:
|
|
104
|
+
return False # Non-fatal — never break the session
|
|
105
|
+
|
|
106
|
+
|
|
68
107
|
def main() -> int:
|
|
69
108
|
input_data = json.loads(sys.stdin.read())
|
|
70
109
|
project_dir = os.environ.get("CODEBUDDY_PROJECT_DIR") or os.environ.get(
|
|
71
110
|
"CLAUDE_PROJECT_DIR", input_data.get("cwd", "")
|
|
72
111
|
)
|
|
73
112
|
|
|
113
|
+
# Auto-update: silently regenerate files if modus CLI was upgraded since last init/update
|
|
114
|
+
was_updated = _silent_auto_update(project_dir)
|
|
115
|
+
|
|
74
116
|
context_parts: list[str] = []
|
|
75
117
|
|
|
76
118
|
# 1. knowledge-catalog — Level 1 index (~200 tokens)
|
|
@@ -113,10 +155,18 @@ def main() -> int:
|
|
|
113
155
|
if not context_parts:
|
|
114
156
|
output = {"continue": True}
|
|
115
157
|
else:
|
|
158
|
+
update_notice = "\n🔄 [Modus 已自动升级] 平台文件已静默刷新至最新版本。" if was_updated else ""
|
|
116
159
|
additional_context = (
|
|
117
160
|
"=== Modus 项目上下文(自动注入)===\n\n"
|
|
118
161
|
+ "\n\n".join(context_parts)
|
|
119
|
-
+
|
|
162
|
+
+ update_notice
|
|
163
|
+
+ "\n\n**[自动 Vibe 模式已启用]**\n"
|
|
164
|
+
"本会话无需用户输入 /modus:vibe,请自动以「懂项目的开发者」身份响应:\n"
|
|
165
|
+
"1. 根据用户请求,从知识目录中匹配相关业务域\n"
|
|
166
|
+
"2. 按需加载对应的 Business Skill(.codebuddy/skills/modus-biz-*/SKILL.md)\n"
|
|
167
|
+
"3. 以 Skill 中的业务规则、命名约定和已知坑作为约束回答或编码\n"
|
|
168
|
+
"4. 若用户显式运行 /modus:plan、/modus:spec 或 /modus:harness,切换到对应模式\n"
|
|
169
|
+
"完整 Vibe 工作流程见 modus-vibe Skill。"
|
|
120
170
|
)
|
|
121
171
|
output = {
|
|
122
172
|
"continue": True,
|
|
@@ -164,6 +164,76 @@ SubAgent-3 (user域) ──► 扫描用户相关文件 ──► 生成 modu
|
|
|
164
164
|
|
|
165
165
|
每个 Skill 文件遵循 Business Skill 标准格式(含 maturity/last_referenced/usage_count 字段)。
|
|
166
166
|
|
|
167
|
+
### Step 3.5:多平台同步(Business Skills)
|
|
168
|
+
|
|
169
|
+
Business Skills 全部生成后,读取 `modus/config.yaml` 的 `platforms` 字段,将每个业务 Skill 同步到其他已选平台。
|
|
170
|
+
|
|
171
|
+
**读取 platforms 配置:**
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
# 检查已选平台
|
|
175
|
+
grep -A5 "^platforms:" modus/config.yaml
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
**各平台同步规则(仅处理 codebuddy 以外的平台):**
|
|
179
|
+
|
|
180
|
+
#### Claude Code(`.claude/agents/modus-biz-{domain}.md`)
|
|
181
|
+
|
|
182
|
+
对每个业务域,创建 Claude Sub-Agent 定义文件:
|
|
183
|
+
|
|
184
|
+
```markdown
|
|
185
|
+
---
|
|
186
|
+
name: modus-biz-{domain}
|
|
187
|
+
description: Business knowledge for the {domain} domain — {核心职责一句话}
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
{.codebuddy/skills/modus-biz-{domain}/SKILL.md 的完整内容}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
#### Cursor(`.cursor/rules/modus-biz-{domain}.mdc`)
|
|
194
|
+
|
|
195
|
+
对每个业务域,创建 Cursor 规则文件(带 frontmatter):
|
|
196
|
+
|
|
197
|
+
```markdown
|
|
198
|
+
---
|
|
199
|
+
description: "Business rules and conventions for the {domain} domain — {核心职责一句话}"
|
|
200
|
+
alwaysApply: false
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
{.codebuddy/skills/modus-biz-{domain}/SKILL.md 的完整内容}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
#### GitHub Copilot(`.github/copilot-instructions.md`)
|
|
207
|
+
|
|
208
|
+
将每个业务域的 Skill 内容以带标记的段落形式追加或更新(幂等):
|
|
209
|
+
|
|
210
|
+
```markdown
|
|
211
|
+
<!-- modus:biz-skill:{domain}:start -->
|
|
212
|
+
|
|
213
|
+
### Business Skill: {domain}
|
|
214
|
+
|
|
215
|
+
{.codebuddy/skills/modus-biz-{domain}/SKILL.md 的完整内容}
|
|
216
|
+
|
|
217
|
+
<!-- modus:biz-skill:{domain}:end -->
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
**若文件中已存在对应域的标记段落,替换其内容(不重复追加)。**
|
|
221
|
+
|
|
222
|
+
**同步完成输出:**
|
|
223
|
+
|
|
224
|
+
```
|
|
225
|
+
📡 多平台同步:
|
|
226
|
+
✓ Claude .claude/agents/modus-biz-order.md
|
|
227
|
+
✓ Claude .claude/agents/modus-biz-payment.md
|
|
228
|
+
✓ Cursor .cursor/rules/modus-biz-order.mdc
|
|
229
|
+
✓ Cursor .cursor/rules/modus-biz-payment.mdc
|
|
230
|
+
✓ Copilot .github/copilot-instructions.md(已更新 2 个域段落)
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
若 `platforms` 只包含 `codebuddy`(或字段不存在),跳过此步骤。
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
167
237
|
### Step 4:生成团队约定 Skill(Layer 0-T)
|
|
168
238
|
|
|
169
239
|
调用「Skills Builder SubAgent」(模式 E:团队约定初始化):
|
|
@@ -274,6 +344,11 @@ constitution:
|
|
|
274
344
|
- .codebuddy/skills/modus-biz-payment/SKILL.md [draft]
|
|
275
345
|
- ...
|
|
276
346
|
|
|
347
|
+
多平台同步(来自 modus/config.yaml platforms 配置):
|
|
348
|
+
- Claude Code → .claude/agents/modus-biz-*.md(N 个域)
|
|
349
|
+
- Cursor → .cursor/rules/modus-biz-*.mdc(N 个域)
|
|
350
|
+
- Copilot → .github/copilot-instructions.md(N 个域段落)
|
|
351
|
+
|
|
277
352
|
初始化基础 Skill:
|
|
278
353
|
- .codebuddy/skills/modus-team-conventions/SKILL.md (Layer 0-T) [draft]
|
|
279
354
|
└─ 融合来源:CLAUDE.md, .cursor/rules/, CONTRIBUTING.md, 代码注解模式
|
|
@@ -393,6 +393,28 @@ design.md ────────────► design-brief.md ────
|
|
|
393
393
|
maturity 变化: modus-biz-order draft→verified
|
|
394
394
|
```
|
|
395
395
|
|
|
396
|
+
### Step 7.5:多平台 Skill 同步(后置)
|
|
397
|
+
|
|
398
|
+
知识回写完成后,对所有本次**更新或新建**的业务 Skill 执行多平台同步。
|
|
399
|
+
|
|
400
|
+
**逻辑:**
|
|
401
|
+
|
|
402
|
+
1. 读取 `modus/config.yaml` 的 `platforms` 字段
|
|
403
|
+
2. 对受影响的每个业务域(Step 4 前置更新 / 新建的域),按以下规则同步:
|
|
404
|
+
- **Claude** → 写入 `.claude/agents/modus-biz-{domain}.md`(覆盖旧版本)
|
|
405
|
+
- **Cursor** → 写入 `.cursor/rules/modus-biz-{domain}.mdc`(带 frontmatter,覆盖旧版本)
|
|
406
|
+
- **Copilot** → 在 `.github/copilot-instructions.md` 中替换对应域的标记段落
|
|
407
|
+
3. 若 `platforms` 只含 `codebuddy`(或字段不存在),跳过
|
|
408
|
+
|
|
409
|
+
**输出示例:**
|
|
410
|
+
|
|
411
|
+
```
|
|
412
|
+
📡 多平台 Skill 同步:
|
|
413
|
+
✓ Claude .claude/agents/modus-biz-order.md(已更新)
|
|
414
|
+
✓ Cursor .cursor/rules/modus-biz-order.mdc(已更新)
|
|
415
|
+
✓ Copilot .github/copilot-instructions.md(order 域段落已更新)
|
|
416
|
+
```
|
|
417
|
+
|
|
396
418
|
---
|
|
397
419
|
|
|
398
420
|
## 产出物目录结构
|
|
@@ -390,6 +390,28 @@ grep -l "### Requirement:" modus/specs/{domain}/spec.md 2>/dev/null
|
|
|
390
390
|
- knowledge-catalog.md 已更新(order 域 maturity: draft→verified)
|
|
391
391
|
```
|
|
392
392
|
|
|
393
|
+
### Step 7.5:多平台 Skill 同步(后置)
|
|
394
|
+
|
|
395
|
+
知识提取与回写完成后,对本次涉及的业务域执行多平台同步。
|
|
396
|
+
|
|
397
|
+
**逻辑:**
|
|
398
|
+
|
|
399
|
+
1. 读取 `modus/config.yaml` 的 `platforms` 字段
|
|
400
|
+
2. 对 Step 4 前置更新 / 新建的每个业务域,按以下规则同步:
|
|
401
|
+
- **Claude** → 写入 `.claude/agents/modus-biz-{domain}.md`(覆盖旧版本)
|
|
402
|
+
- **Cursor** → 写入 `.cursor/rules/modus-biz-{domain}.mdc`(带 frontmatter,覆盖旧版本)
|
|
403
|
+
- **Copilot** → 在 `.github/copilot-instructions.md` 中替换对应域的标记段落
|
|
404
|
+
3. 若 `platforms` 只含 `codebuddy`(或字段不存在),跳过
|
|
405
|
+
|
|
406
|
+
**输出示例:**
|
|
407
|
+
|
|
408
|
+
```
|
|
409
|
+
📡 多平台 Skill 同步:
|
|
410
|
+
✓ Claude .claude/agents/modus-biz-order.md(已更新)
|
|
411
|
+
✓ Cursor .cursor/rules/modus-biz-order.mdc(已更新)
|
|
412
|
+
✓ Copilot .github/copilot-instructions.md(order 域段落已更新)
|
|
413
|
+
```
|
|
414
|
+
|
|
393
415
|
---
|
|
394
416
|
|
|
395
417
|
## 主规格库结构
|