@dotdotgod/opencode 0.0.0-stage → 0.6.6
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/LICENSE +94 -0
- package/README.md +84 -2
- package/commands/impact.md +26 -0
- package/commands/init.md +38 -0
- package/commands/load.md +28 -0
- package/index.js +1 -0
- package/mcp/cli.mjs +7017 -0
- package/mcp/impact-state.mjs +133 -0
- package/mcp/parsers/IGNORE_LICENSE +21 -0
- package/mcp/parsers/LICENSE +21 -0
- package/mcp/parsers/TYPESCRIPT_LICENSE +21 -0
- package/mcp/parsers/tree-sitter-bash.wasm +0 -0
- package/mcp/parsers/tree-sitter-c-sharp.wasm +0 -0
- package/mcp/parsers/tree-sitter-cpp.wasm +0 -0
- package/mcp/parsers/tree-sitter-css.wasm +0 -0
- package/mcp/parsers/tree-sitter-go.wasm +0 -0
- package/mcp/parsers/tree-sitter-ini.wasm +0 -0
- package/mcp/parsers/tree-sitter-java.wasm +0 -0
- package/mcp/parsers/tree-sitter-javascript.wasm +0 -0
- package/mcp/parsers/tree-sitter-php.wasm +0 -0
- package/mcp/parsers/tree-sitter-powershell.wasm +0 -0
- package/mcp/parsers/tree-sitter-python.wasm +0 -0
- package/mcp/parsers/tree-sitter-regex.wasm +0 -0
- package/mcp/parsers/tree-sitter-ruby.wasm +0 -0
- package/mcp/parsers/tree-sitter-rust.wasm +0 -0
- package/mcp/parsers/tree-sitter-tsx.wasm +0 -0
- package/mcp/parsers/tree-sitter-typescript.wasm +0 -0
- package/mcp/parsers/tree-sitter.cjs +4075 -0
- package/mcp/parsers/tree-sitter.wasm +0 -0
- package/mcp/server.mjs +23716 -0
- package/package.json +41 -3
- package/server.js +1 -0
- package/skills/doc-first-planning/SKILL.md +30 -0
- package/skills/document-clarify/SKILL.md +41 -0
- package/skills/impact-review/SKILL.md +25 -0
- package/skills/project-initializer/SKILL.md +37 -0
- package/skills/project-initializer/references/agent-docs.md +42 -0
- package/skills/project-initializer/scripts/init_project.sh +525 -0
- package/skills/project-initializer/templates/case-and-evidence.json +219 -0
- package/skills/project-initializer/templates/dotdotgod.config.json +234 -0
- package/skills/project-initializer/templates/policy.json +241 -0
- package/skills/project-initializer/templates/portfolio.json +255 -0
- package/skills/project-initializer/templates/publication.json +265 -0
- package/skills/project-initializer/templates/research.json +287 -0
- package/skills/project-initializer/templates/software.json +234 -0
- package/skills/project-load/SKILL.md +27 -0
- package/src/impact.mjs +25 -0
- package/src/index.mjs +17 -0
- package/src/location.mjs +7 -0
- package/src/mcp.mjs +37 -0
- package/src/plan.mjs +65 -0
- package/src/resources.mjs +28 -0
package/src/plan.mjs
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { homedir } from 'node:os';
|
|
2
|
+
import { join, resolve } from 'node:path';
|
|
3
|
+
import { isLocalSession } from './location.mjs';
|
|
4
|
+
|
|
5
|
+
const start = 'You are in Plan mode. Discuss the plan with the user directly in the conversation. Do not create or update plan files unless the user explicitly asks you to; when they do, write them only in:\n';
|
|
6
|
+
const end = '\nYou remain in Plan mode until the user switches agents. If the user asks you to implement changes, do not do so. Tell them they need to switch agents.';
|
|
7
|
+
const mutationRestriction = '\n\nDo not modify any other files or ask a subagent to do so.\n';
|
|
8
|
+
|
|
9
|
+
export function planGuidance(root) {
|
|
10
|
+
return `You are in Plan mode. Discuss the plan with the user and automatically save a concrete implementation plan, without waiting for a separate file-save request, to:\n${join(root, 'docs/plan/<task-slug>/README.md')}\nUse a kebab-case task directory and English Markdown. Reuse and update the same task plan as decisions change; do not overwrite an unrelated task. Include scope, executable steps, verification and unresolved decisions. Save drafts with unresolved questions; initial clarification and explanation-only conversations need no file. Saving is not execution approval. Do not modify any other files or ask a subagent to do so. Remain in Plan mode until the user switches agents. If asked to implement, tell the user to switch agents first.`;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function rewritePlanReminder(text, root) {
|
|
14
|
+
const marker = text.indexOf(start);
|
|
15
|
+
if (marker < 0) return { text, replaced: false };
|
|
16
|
+
const finish = text.indexOf(end, marker + start.length);
|
|
17
|
+
if (finish < 0) return { text, replaced: false };
|
|
18
|
+
const section = text.slice(marker + start.length, finish);
|
|
19
|
+
const path = section.endsWith(mutationRestriction) ? section.slice(0, -mutationRestriction.length) : section;
|
|
20
|
+
if (!path.endsWith('/.opencode/plan') || path.includes('\n')) return { text, replaced: false };
|
|
21
|
+
return { text: text.slice(0, marker) + planGuidance(root) + text.slice(finish + end.length), replaced: true };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Replace the shipped exception in place, not at the end of the rules array.
|
|
25
|
+
// Later user/global/agent deny rules must continue to win.
|
|
26
|
+
export function redirectPlanPermissions(agent, root, home = homedir()) {
|
|
27
|
+
const rules = agent.permissions;
|
|
28
|
+
const index = rules.findIndex((rule, i) => rule.action === 'edit' && rule.effect === 'allow' &&
|
|
29
|
+
rule.resource === join(home, '.opencode/plan/*') && rules[i - 1]?.action === 'edit' &&
|
|
30
|
+
rules[i - 1]?.resource === '*' && rules[i - 1]?.effect === 'deny');
|
|
31
|
+
if (index < 0) return false;
|
|
32
|
+
rules.splice(index, 1, { action: 'edit', resource: `${resolve(root)}/docs/plan/*`, effect: 'allow' },
|
|
33
|
+
{ action: 'edit', resource: 'docs/plan/*', effect: 'allow' });
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export async function registerPlan(ctx, root) {
|
|
38
|
+
// This is an explicit compatibility integration, not a public path setting.
|
|
39
|
+
const supported = ctx.app.version.replace(/^v/, '') === '2.1.2';
|
|
40
|
+
let redirected = false;
|
|
41
|
+
if (supported) await ctx.agent.transform(editor => {
|
|
42
|
+
redirected = false;
|
|
43
|
+
editor.update('plan', agent => { redirected = redirectPlanPermissions(agent, root); });
|
|
44
|
+
});
|
|
45
|
+
await ctx.session.hook('context', async event => {
|
|
46
|
+
if (event.agent !== 'plan' || !await isLocalSession(ctx, event.sessionID)) return;
|
|
47
|
+
let replaced = false;
|
|
48
|
+
if (supported && redirected) {
|
|
49
|
+
for (const message of event.messages) {
|
|
50
|
+
if (message.role !== 'user') continue;
|
|
51
|
+
if (typeof message.content === 'string') {
|
|
52
|
+
const result = rewritePlanReminder(message.content, root);
|
|
53
|
+
message.content = result.text; replaced ||= result.replaced;
|
|
54
|
+
} else if (Array.isArray(message.content)) {
|
|
55
|
+
for (const part of message.content) if (part.type === 'text') {
|
|
56
|
+
const result = rewritePlanReminder(part.text, root);
|
|
57
|
+
part.text = result.text; replaced ||= result.replaced;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (replaced) event.system.push({ type: 'text', text: planGuidance(root) });
|
|
63
|
+
else event.system.push({ type: 'text', text: '[DOTDOTGOD PLAN COMPATIBILITY] Automatic project-plan saving is unavailable for this OpenCode version/reminder or permission layout. Tell the user; retain native Plan restrictions and do not claim a plan was saved.' });
|
|
64
|
+
});
|
|
65
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { readFileSync, readdirSync } from 'node:fs';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
|
|
4
|
+
export function markdown(path) {
|
|
5
|
+
const source = readFileSync(path, 'utf8');
|
|
6
|
+
const match = source.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n/);
|
|
7
|
+
const description = match?.[1].match(/^description: (.+)$/m)?.[1];
|
|
8
|
+
return { description, content: match ? source.slice(match[0].length).trim() : source.trim() };
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export async function registerResources(ctx) {
|
|
12
|
+
const skills = readdirSync(new URL('../skills/', import.meta.url)).map(id => {
|
|
13
|
+
const path = fileURLToPath(new URL(`../skills/${id}/SKILL.md`, import.meta.url));
|
|
14
|
+
return { id, name: id, path, ...markdown(path) };
|
|
15
|
+
});
|
|
16
|
+
await ctx.skill.transform(editor => {
|
|
17
|
+
for (const skill of skills) if (!editor.get(skill.id)) editor.add(skill);
|
|
18
|
+
});
|
|
19
|
+
const commands = ['load', 'init', 'impact'].map(id => ({ id, ...markdown(new URL(`../commands/${id}.md`, import.meta.url)) }));
|
|
20
|
+
await ctx.command.transform(editor => {
|
|
21
|
+
for (const command of commands) editor.add({
|
|
22
|
+
name: `dd:${command.id}`, description: command.description,
|
|
23
|
+
execute: async ({ sessionID, prompt, delivery }) => {
|
|
24
|
+
await ctx.session.prompt({ ...prompt, sessionID, delivery, text: command.content.replaceAll('$ARGUMENTS', () => prompt.text ?? '') });
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
}
|