@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
- // Injects the homie personality into every model call's system context at the
4
- // active level, persists /homie level switches, registers the /homie command
5
- // and skill. Reuses the shared instruction builder so Claude Code, Codex, and
6
- // OpenCode all read one source of truth.
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 { getChillInstructions } = require('../../hooks/homie-instructions');
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. An unrecognized level leaves the current one alone. Bare /homie
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; // bare /homie: report-only when active
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
- persistLevel(prompt.text);
94
- const level = readLevel();
95
- const text = level === 'off'
96
- ? 'Homie is off — back to the normal tone.'
97
- : 'HOMIE MODE — level: ' + level + '.\n\n' + getChillInstructions(level);
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: getChillInstructions(level) });
130
+ event.system.push({ type: 'text', text: getHomieInstructions(level) });
109
131
  });
110
132
  },
111
133
  };
@@ -1,2 +1,2 @@
1
1
  description = "Switch homie personality level (off/yo/dawg/mafa)"
2
- prompt = "Switch to homie {{args}} mode. With no level, turn the voice on at yo, or report the current level if already on. This is a communication personality only: casual/warm at yo, blunt with light roasting and rare mild swearing (damn, hell, crap) at dawg, very informal and sarcastic with natural (never forced) profanity at mafa. Voice lives in chat prose only — code, diffs, commands, commit messages, and permission warnings stay plain. Technical recommendations, tools, permissions, and code correctness never change."
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."
@@ -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 { getChillInstructions } = require('./homie-instructions');
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 — skip activation entirely, don't write flag or emit rules.
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 { writeHookOutput('SessionStart', 'off', 'HOMIE DEFAULT OFF — start normal.'); } catch (e) {}
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 = getChillInstructions(level);
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 getChillInstructions(level) {
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
- getChillInstructions,
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 { getChillInstructions } = require('./homie-instructions');
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' + getChillInstructions(level));
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.0",
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
+ }