@kpnpm/homie 0.1.0 → 0.1.2
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.
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// homie — OpenCode V2 plugin.
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
3
|
+
// Registers the /homie command and skill, persists level switches, and
|
|
4
|
+
// injects the homie personality into every model call's system context at
|
|
5
|
+
// the active level. Command replies are one plain line — the full ruleset
|
|
6
|
+
// is delivered invisibly through the context hook, never dumped into chat.
|
|
7
7
|
//
|
|
8
8
|
// Add to your opencode.json:
|
|
9
9
|
// { "plugins": ["@kpnpm/homie"] }
|
|
@@ -19,8 +19,8 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
|
19
19
|
|
|
20
20
|
// The shared instruction builder is CommonJS; bridge to it from this ES module.
|
|
21
21
|
const require = createRequire(import.meta.url);
|
|
22
|
-
const {
|
|
23
|
-
const { getDefaultLevel, normalizeLevel } = require('../../hooks/homie-config');
|
|
22
|
+
const { getHomieInstructions } = require('../../hooks/homie-instructions');
|
|
23
|
+
const { getDefaultLevel, normalizeLevel, writeDefaultLevel } = require('../../hooks/homie-config');
|
|
24
24
|
|
|
25
25
|
// OpenCode has no flag-file convention of its own; keep the level beside its config.
|
|
26
26
|
const statePath = path.join(
|
|
@@ -42,16 +42,17 @@ function writeLevel(level) {
|
|
|
42
42
|
fs.writeFileSync(statePath, level);
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
// Returns the applied level, null for an unrecognized level, or undefined
|
|
46
|
+
// when nothing changed (bare /homie while already on → report-only).
|
|
45
47
|
// `off` is persisted like any level; the context hook reads it and stays
|
|
46
|
-
// silent.
|
|
47
|
-
// turns the voice on at yo (per SKILL.md); with any level active the command
|
|
48
|
-
// just reports — the execute() reply shows the current level.
|
|
48
|
+
// silent. Bare /homie turns the voice on at yo (per SKILL.md).
|
|
49
49
|
function persistLevel(args) {
|
|
50
50
|
const wanted = String(args == null ? '' : args).trim();
|
|
51
|
-
if (!wanted && readLevel() !== 'off') return;
|
|
51
|
+
if (!wanted && readLevel() !== 'off') return undefined;
|
|
52
52
|
const level = wanted ? normalizeLevel(wanted) : 'yo';
|
|
53
|
-
if (!level) return;
|
|
53
|
+
if (!level) return null;
|
|
54
54
|
writeLevel(level);
|
|
55
|
+
return level;
|
|
55
56
|
}
|
|
56
57
|
|
|
57
58
|
function readSkill() {
|
|
@@ -90,11 +91,32 @@ export default {
|
|
|
90
91
|
name: 'homie',
|
|
91
92
|
description: 'Switch personality level (off/yo/dawg/mafa)',
|
|
92
93
|
execute: async ({ sessionID, prompt, delivery }) => {
|
|
93
|
-
|
|
94
|
-
const
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
94
|
+
const wanted = String(prompt.text || '').trim();
|
|
95
|
+
const words = wanted.split(/\s+/).filter(Boolean);
|
|
96
|
+
const first = words[0] || '';
|
|
97
|
+
let text;
|
|
98
|
+
|
|
99
|
+
if (first === 'default') {
|
|
100
|
+
const applied = words[1] ? writeDefaultLevel(words[1]) : null;
|
|
101
|
+
text = applied
|
|
102
|
+
? 'Homie default set: ' + applied + '. New sessions start at ' + applied + '.'
|
|
103
|
+
: 'Usage: /homie default <level>. Levels: off, yo, dawg, mafa.';
|
|
104
|
+
} else {
|
|
105
|
+
const applied = persistLevel(first);
|
|
106
|
+
const level = readLevel();
|
|
107
|
+
if (first && applied === null) {
|
|
108
|
+
text = 'Unknown level "' + first + '". Levels: off, yo, dawg, mafa.';
|
|
109
|
+
} else if (level === 'off') {
|
|
110
|
+
text = 'Homie off.';
|
|
111
|
+
} else {
|
|
112
|
+
text = 'Homie mode: ' + level + '.';
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// One plain line only. The context hook below injects the full
|
|
117
|
+
// ruleset into the system context of this same model call, so the
|
|
118
|
+
// confirmation turn is already in voice — without dumping the
|
|
119
|
+
// ruleset into the chat.
|
|
98
120
|
await ctx.session.prompt({ ...prompt, sessionID, text, delivery });
|
|
99
121
|
},
|
|
100
122
|
});
|
|
@@ -105,7 +127,7 @@ export default {
|
|
|
105
127
|
await ctx.session.hook('context', (event) => {
|
|
106
128
|
const level = readLevel();
|
|
107
129
|
if (level === 'off') return;
|
|
108
|
-
event.system.push({ type: 'text', text:
|
|
130
|
+
event.system.push({ type: 'text', text: getHomieInstructions(level) });
|
|
109
131
|
});
|
|
110
132
|
},
|
|
111
133
|
};
|
package/commands/homie.toml
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
description = "Switch homie personality level (off/yo/dawg/mafa)"
|
|
2
|
-
prompt = "Switch
|
|
2
|
+
prompt = "Switch homie voice level: {{args}} (yo casual / dawg blunt, rare mild swearing / mafa very informal, natural profanity / off normal). No level: report the current one, or start at yo if off. Chat voice only — technical answers, code, and commands never change."
|
package/hooks/homie-activate.js
CHANGED
|
@@ -9,21 +9,23 @@
|
|
|
9
9
|
const fs = require('fs');
|
|
10
10
|
const path = require('path');
|
|
11
11
|
const { getDefaultLevel, isShellSafe } = require('./homie-config');
|
|
12
|
-
const {
|
|
12
|
+
const { getHomieInstructions } = require('./homie-instructions');
|
|
13
13
|
const {
|
|
14
14
|
setLevel,
|
|
15
15
|
writeHookOutput,
|
|
16
16
|
isCodex,
|
|
17
17
|
isCopilot,
|
|
18
18
|
getClaudeDir,
|
|
19
|
-
statePath,
|
|
20
19
|
} = require('./homie-runtime');
|
|
21
20
|
|
|
22
21
|
const level = getDefaultLevel();
|
|
23
22
|
|
|
24
|
-
// "off" default —
|
|
23
|
+
// "off" default — persist off like any level: a stale flag from a previous
|
|
24
|
+
// session would otherwise keep the statusline badge showing the old level
|
|
25
|
+
// while this session is normal. Stay silent: emitting a notice every session
|
|
26
|
+
// is context noise for a plugin that is off.
|
|
25
27
|
if (level === 'off') {
|
|
26
|
-
try {
|
|
28
|
+
try { setLevel('off'); } catch (e) {}
|
|
27
29
|
process.exit(0);
|
|
28
30
|
}
|
|
29
31
|
|
|
@@ -35,7 +37,7 @@ try {
|
|
|
35
37
|
}
|
|
36
38
|
|
|
37
39
|
// 2. Emit the homie ruleset at the default level.
|
|
38
|
-
let output =
|
|
40
|
+
let output = getHomieInstructions(level);
|
|
39
41
|
|
|
40
42
|
// 3. Detect missing statusline config — nudge Claude to help set it up.
|
|
41
43
|
// Codex and Copilot don't read Claude settings.json; skip the nudge there.
|
|
@@ -97,7 +97,7 @@ function getFallbackInstructions(level) {
|
|
|
97
97
|
'Switch: /homie yo|dawg|mafa.';
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
function
|
|
100
|
+
function getHomieInstructions(level) {
|
|
101
101
|
const effectiveLevel = normalizeLevel(level);
|
|
102
102
|
if (!effectiveLevel || effectiveLevel === 'off') return '';
|
|
103
103
|
|
|
@@ -112,5 +112,5 @@ function getChillInstructions(level) {
|
|
|
112
112
|
module.exports = {
|
|
113
113
|
filterSkillBodyForLevel,
|
|
114
114
|
getFallbackInstructions,
|
|
115
|
-
|
|
115
|
+
getHomieInstructions,
|
|
116
116
|
};
|
|
@@ -6,10 +6,9 @@ const { getDefaultLevel, writeDefaultLevel, isDeactivationCommand } = require('.
|
|
|
6
6
|
const {
|
|
7
7
|
readLevel,
|
|
8
8
|
setLevel,
|
|
9
|
-
clearLevel,
|
|
10
9
|
writeHookOutput,
|
|
11
10
|
} = require('./homie-runtime');
|
|
12
|
-
const {
|
|
11
|
+
const { getHomieInstructions } = require('./homie-instructions');
|
|
13
12
|
|
|
14
13
|
let input = '';
|
|
15
14
|
let done = false;
|
|
@@ -75,7 +74,7 @@ function finish() {
|
|
|
75
74
|
// Deliver the new level's ruleset along with the confirmation so the
|
|
76
75
|
// switch turn itself is already in voice.
|
|
77
76
|
const header = 'HOMIE MODE CHANGED — level: ' + level;
|
|
78
|
-
writeHookOutput('UserPromptSubmit', level, header + '\n\n' +
|
|
77
|
+
writeHookOutput('UserPromptSubmit', level, header + '\n\n' + getHomieInstructions(level));
|
|
79
78
|
} else if (level === 'off') {
|
|
80
79
|
// Persist `off` like any level (plan lesson #7): clearing the flag
|
|
81
80
|
// races the default logic — an absent flag reads as the default level.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kpnpm/homie",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Personality layer for coding agents. Same code, different voice: yo / dawg / mafa / off.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"opencode-plugin",
|
|
@@ -43,4 +43,4 @@
|
|
|
43
43
|
"publishConfig": {
|
|
44
44
|
"access": "public"
|
|
45
45
|
}
|
|
46
|
-
}
|
|
46
|
+
}
|