niahere 0.2.35 → 0.2.37
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/README.md +41 -25
- package/package.json +1 -1
- package/skills/taskmaster/SKILL.md +104 -0
- package/src/commands/init.ts +16 -2
- package/src/commands/service.ts +13 -2
- package/src/core/daemon.ts +16 -2
package/README.md
CHANGED
|
@@ -1,11 +1,25 @@
|
|
|
1
1
|
# nia
|
|
2
2
|
|
|
3
|
-
A personal AI
|
|
3
|
+
A personal AI agent you fork and make your own. Small enough to understand, built for one user. Powered by Claude Agent SDK.
|
|
4
4
|
|
|
5
5
|
- npm package: [`niahere`](https://www.npmjs.com/package/niahere)
|
|
6
6
|
- CLI command: `nia`
|
|
7
7
|
- Website: [niahere.com](https://niahere.com)
|
|
8
8
|
|
|
9
|
+
## Philosophy
|
|
10
|
+
|
|
11
|
+
**Small enough to understand.** One process, a few source files. No microservices, no message queues, no abstraction layers. Have Claude Code walk you through it.
|
|
12
|
+
|
|
13
|
+
**Built for one user.** This isn't a framework. It's working software that fits your exact needs. You fork it and have Claude Code make it match your exact needs.
|
|
14
|
+
|
|
15
|
+
**Customization = code changes.** No configuration sprawl. Want different behavior? Modify the code. The codebase is small enough that this is safe.
|
|
16
|
+
|
|
17
|
+
**AI-native.** No installation wizard; Claude Code guides setup. No monitoring dashboard; ask Claude what's happening. No debugging tools; describe the problem, Claude fixes it.
|
|
18
|
+
|
|
19
|
+
**Skills over features.** Contributors shouldn't add features to the codebase. Instead, they contribute claude code skills like `/add-discord` that transform your fork. You end up with clean code that does exactly what you need.
|
|
20
|
+
|
|
21
|
+
**Best harness, best model.** This runs on Claude Agent SDK, which means you're running Claude Code directly. The harness matters. A bad harness makes even smart models seem dumb, a good harness gives them superpowers.
|
|
22
|
+
|
|
9
23
|
## Quick Start
|
|
10
24
|
|
|
11
25
|
```bash
|
|
@@ -14,6 +28,18 @@ nia init # guided setup (database, channels, persona, visual iden
|
|
|
14
28
|
nia start # starts daemon + registers OS service
|
|
15
29
|
```
|
|
16
30
|
|
|
31
|
+
## What It Supports
|
|
32
|
+
|
|
33
|
+
- **Telegram** — message your agent from your phone, typing indicator while processing
|
|
34
|
+
- **Slack** — Socket Mode bot with thread awareness, thinking emoji, watch channels for proactive monitoring
|
|
35
|
+
- **Terminal chat** — REPL with session resume support
|
|
36
|
+
- **Scheduled jobs** — recurring jobs and crons that run Claude and can message you back
|
|
37
|
+
- **Persona system** — customizable identity, soul, owner profile, and on-demand memory
|
|
38
|
+
- **Skills** — loads skills from multiple directories, invokable as slash commands
|
|
39
|
+
- **Cross-platform service** — launchd (macOS), systemd (Linux), service-aware restart
|
|
40
|
+
- **MCP tools** — 18 tools for job management, messaging, memory, and channel control
|
|
41
|
+
- **Optional integrations** — add Gmail, Discord, and more via skills
|
|
42
|
+
|
|
17
43
|
## Commands
|
|
18
44
|
|
|
19
45
|
```
|
|
@@ -50,30 +76,6 @@ nia channels — show channel status (on/off)
|
|
|
50
76
|
nia channels on / off — enable/disable channels
|
|
51
77
|
```
|
|
52
78
|
|
|
53
|
-
## Features
|
|
54
|
-
|
|
55
|
-
- **Jobs & crons** — jobs run during active hours, crons run 24/7. Stored in PostgreSQL, auto-reload via LISTEN/NOTIFY. One-shot jobs auto-disable after execution. Full JSONL traces with Codex session IDs.
|
|
56
|
-
- **Terminal chat** — REPL with session resume support
|
|
57
|
-
- **Telegram** — bot with access control, typing indicator while processing
|
|
58
|
-
- **Slack** — Socket Mode bot with thinking emoji reactions, thread awareness (auto-listens to follow-ups without @mention), thread context fetching, owner vs non-owner access control, prompt injection defense
|
|
59
|
-
- **Persona system** — customizable identity, soul, owner profile, and on-demand memory
|
|
60
|
-
- **Visual identity** — AI-generated profile pictures via Gemini, customizable during `nia init`
|
|
61
|
-
- **Cross-platform service** — launchd (macOS), systemd (Linux), service-aware restart
|
|
62
|
-
- **Skills** — loads skills from `~/.shared/skills/`, `~/.claude/skills/`, `~/.codex/skills/`, and bundled skills
|
|
63
|
-
- **Dev mode** — `nia channels off` disables Telegram/Slack for local development without conflicts
|
|
64
|
-
|
|
65
|
-
## Updating
|
|
66
|
-
|
|
67
|
-
```bash
|
|
68
|
-
npm i -g niahere # pulls the latest version from npm
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
To publish a new version after making changes:
|
|
72
|
-
|
|
73
|
-
```bash
|
|
74
|
-
npm run release # bumps patch version, publishes to npm, pushes git tag
|
|
75
|
-
```
|
|
76
|
-
|
|
77
79
|
## Architecture
|
|
78
80
|
|
|
79
81
|
All config and data lives in `~/.niahere/`:
|
|
@@ -93,6 +95,14 @@ All config and data lives in `~/.niahere/`:
|
|
|
93
95
|
nia.pid, daemon.log, cron-state.json, cron-audit.jsonl
|
|
94
96
|
```
|
|
95
97
|
|
|
98
|
+
## Contributing
|
|
99
|
+
|
|
100
|
+
**Don't add features. Add skills.**
|
|
101
|
+
|
|
102
|
+
If you want to add Discord support, don't create a PR that adds Discord alongside Telegram. Instead, contribute a skill folder (`skills/add-discord/SKILL.md`) that teaches Claude Code how to transform a nia installation to use Discord.
|
|
103
|
+
|
|
104
|
+
Users then run `/add-discord` on their fork and get clean code that does exactly what they need, not a bloated system trying to support every use case.
|
|
105
|
+
|
|
96
106
|
## Requirements
|
|
97
107
|
|
|
98
108
|
- [Bun](https://bun.sh) runtime (auto-installed if missing)
|
|
@@ -101,6 +111,12 @@ All config and data lives in `~/.niahere/`:
|
|
|
101
111
|
- Gemini API key (optional, for image generation — `nia config set gemini_api_key ...`)
|
|
102
112
|
- OpenAI API key (optional, for image generation — `nia config set openai_api_key ...`)
|
|
103
113
|
|
|
114
|
+
## Updating
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
npm i -g niahere # pulls the latest version from npm
|
|
118
|
+
```
|
|
119
|
+
|
|
104
120
|
## Author
|
|
105
121
|
|
|
106
122
|
Aman ([amankumar.ai](https://amankumar.ai))
|
package/package.json
CHANGED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: taskmaster
|
|
3
|
+
description: |
|
|
4
|
+
Completion guard that prevents premature task abandonment. Use when wrapping up
|
|
5
|
+
any non-trivial task, before claiming work is "done", or when you catch yourself
|
|
6
|
+
writing a summary instead of finishing the work. Also use when the user says
|
|
7
|
+
"don't stop early", "finish everything", "make sure it's actually done", or
|
|
8
|
+
"verify completion". Adapted from github.com/blader/taskmaster.
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Taskmaster
|
|
12
|
+
|
|
13
|
+
Completion guard. Progress is not completion. Before you stop, prove the work is done.
|
|
14
|
+
|
|
15
|
+
## When to Invoke
|
|
16
|
+
|
|
17
|
+
- Before claiming any non-trivial task is complete
|
|
18
|
+
- When you're about to write a summary of what you did
|
|
19
|
+
- When you notice yourself using phrases like "significant progress", "mostly done", or "remaining work would require..."
|
|
20
|
+
- When the user explicitly asks you to finish everything
|
|
21
|
+
|
|
22
|
+
## The Completion Checklist
|
|
23
|
+
|
|
24
|
+
Run every item. Do not skip any. Do not summarize — execute.
|
|
25
|
+
|
|
26
|
+
### 1. Goal Confrontation (Do This First)
|
|
27
|
+
|
|
28
|
+
Answer these three questions explicitly in your response:
|
|
29
|
+
|
|
30
|
+
a. **What is the user's stated goal or success criterion?** Write it out verbatim.
|
|
31
|
+
b. **Is it achieved RIGHT NOW?** Answer "yes" or "no". Not "partially", not "mostly", not "significant progress was made". Yes or no.
|
|
32
|
+
c. **If no:** you are NOT DONE. Go do more work.
|
|
33
|
+
|
|
34
|
+
The ONLY exception is if the user explicitly told you to stop or deprioritized the goal. There is no other valid reason to stop.
|
|
35
|
+
|
|
36
|
+
### 2. Re-read the Original Request
|
|
37
|
+
|
|
38
|
+
Go back to the user's original message(s). List every discrete request or acceptance criterion. For each one, confirm it is **fully addressed** — not started, not in progress, FULLY done.
|
|
39
|
+
|
|
40
|
+
If the user changed their mind or withdrew a request, treat it as resolved.
|
|
41
|
+
|
|
42
|
+
### 3. Check the Task List
|
|
43
|
+
|
|
44
|
+
Review every task. Any task not marked completed? Do it now — unless the user said to skip it.
|
|
45
|
+
|
|
46
|
+
### 4. Check the Plan
|
|
47
|
+
|
|
48
|
+
Walk through each step of the plan, INCLUDING verification steps. Any step skipped or partially done? Finish it.
|
|
49
|
+
|
|
50
|
+
If the plan includes verification steps (builds, tests, lints, type-checks, smoke tests), you MUST actually run them and see them pass. Do not skip them or claim they pass without evidence.
|
|
51
|
+
|
|
52
|
+
### 5. Check for Errors
|
|
53
|
+
|
|
54
|
+
Did anything fail or remain unfinished? Fix it. This applies to ALL types of problems — logic errors, missing functionality, incomplete refactors, broken scripts, configuration issues, or anything else that prevents the work from being fully done.
|
|
55
|
+
|
|
56
|
+
### 6. Check for Loose Ends
|
|
57
|
+
|
|
58
|
+
- TODO comments you left behind?
|
|
59
|
+
- Placeholder code?
|
|
60
|
+
- Missing tests for new code?
|
|
61
|
+
- Untested changes?
|
|
62
|
+
- Follow-ups you noted but didn't act on?
|
|
63
|
+
|
|
64
|
+
If any exist, resolve them now.
|
|
65
|
+
|
|
66
|
+
### 7. Check for Blockers
|
|
67
|
+
|
|
68
|
+
If something is blocking you, do NOT give up. Try a different approach, read more code, search for examples, re-examine your assumptions.
|
|
69
|
+
|
|
70
|
+
"I didn't cause this bug" is not an excuse to stop — if it blocks your task, fix it. You own the outcome, not just your diff.
|
|
71
|
+
|
|
72
|
+
## Anti-Rationalization Guide
|
|
73
|
+
|
|
74
|
+
These are NOT valid reasons to stop:
|
|
75
|
+
|
|
76
|
+
| Rationalization | Reality |
|
|
77
|
+
|---|---|
|
|
78
|
+
| "Diminishing returns" | The goal isn't met. Keep working. |
|
|
79
|
+
| "Significant progress was made" | Progress is not completion. |
|
|
80
|
+
| "Would require broader architectural changes" | Then make them. |
|
|
81
|
+
| "No single dominant hotspot" | Keep looking. |
|
|
82
|
+
| "Tried N approaches" | Try N+1. |
|
|
83
|
+
| "I can't do X" | You haven't tried X yet. |
|
|
84
|
+
| "This is a pre-existing issue" | If it blocks your task, it's your issue. |
|
|
85
|
+
| "The remaining work is complex" | That's not a reason to stop. That's a description of your job. |
|
|
86
|
+
|
|
87
|
+
## Do Not Narrate — Execute
|
|
88
|
+
|
|
89
|
+
If any incomplete work remains, your ONLY job is to DO that work right now.
|
|
90
|
+
|
|
91
|
+
- Do NOT respond by explaining what the remaining tasks are
|
|
92
|
+
- Do NOT describe their complexity or list their dependencies
|
|
93
|
+
- Do NOT ask for permission to proceed
|
|
94
|
+
- Do NOT write summaries of what is left
|
|
95
|
+
|
|
96
|
+
Open files, write code, run commands, fix bugs. Act.
|
|
97
|
+
|
|
98
|
+
## User Instructions Override
|
|
99
|
+
|
|
100
|
+
The user's latest instructions always take priority. If the user said to stop, move on, or skip something — respect that. Do not force completion of work the user no longer wants.
|
|
101
|
+
|
|
102
|
+
## Attribution
|
|
103
|
+
|
|
104
|
+
Core ideas adapted from [Taskmaster](https://github.com/blader/taskmaster) by blader (MIT).
|
package/src/commands/init.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { resetConfig } from "../utils/config";
|
|
|
7
7
|
import { runMigrations } from "../db/migrate";
|
|
8
8
|
import { closeDb } from "../db/connection";
|
|
9
9
|
import { startDaemon, isRunning } from "../core/daemon";
|
|
10
|
+
import { registerService, isServiceInstalled } from "./service";
|
|
10
11
|
import { errMsg } from "../utils/errors";
|
|
11
12
|
import { enrichSlackConfig } from "../cli/channels";
|
|
12
13
|
import yaml from "js-yaml";
|
|
@@ -434,12 +435,25 @@ export async function runInit(): Promise<void> {
|
|
|
434
435
|
|
|
435
436
|
resetConfig();
|
|
436
437
|
|
|
438
|
+
// Install system service (launchd/systemd) for auto-restart on crash
|
|
439
|
+
if (!isServiceInstalled()) {
|
|
440
|
+
try {
|
|
441
|
+
await registerService();
|
|
442
|
+
console.log(`\n \u2713 installed system service (auto-restart on crash)`);
|
|
443
|
+
} catch (err) {
|
|
444
|
+
console.log(`\n \u26a0 could not install system service: ${errMsg(err)}`);
|
|
445
|
+
console.log(` run 'nia service install' manually for auto-restart`);
|
|
446
|
+
}
|
|
447
|
+
} else {
|
|
448
|
+
console.log(`\n - system service already installed`);
|
|
449
|
+
}
|
|
450
|
+
|
|
437
451
|
// Auto-start daemon
|
|
438
452
|
if (!isRunning()) {
|
|
439
453
|
const pid = startDaemon();
|
|
440
|
-
console.log(
|
|
454
|
+
console.log(` \u2713 nia started (pid: ${pid})`);
|
|
441
455
|
} else {
|
|
442
|
-
console.log(
|
|
456
|
+
console.log(` - nia already running`);
|
|
443
457
|
}
|
|
444
458
|
|
|
445
459
|
console.log("\nDone.");
|
package/src/commands/service.ts
CHANGED
|
@@ -8,7 +8,15 @@ const PLIST_NAME = "com.niahere.agent";
|
|
|
8
8
|
const SYSTEMD_UNIT = "niahere.service";
|
|
9
9
|
|
|
10
10
|
function getExecCommand(): [string, string] {
|
|
11
|
-
|
|
11
|
+
const execPath = process.execPath;
|
|
12
|
+
// process.argv[1] may be undefined when called outside the normal CLI flow
|
|
13
|
+
// (e.g. from bun -e or programmatic import). Fall back to the known entry point.
|
|
14
|
+
let cliPath = process.argv[1];
|
|
15
|
+
if (!cliPath || cliPath === "undefined") {
|
|
16
|
+
const { resolve } = require("path");
|
|
17
|
+
cliPath = resolve(import.meta.dir, "../cli/index.ts");
|
|
18
|
+
}
|
|
19
|
+
return [execPath, cliPath];
|
|
12
20
|
}
|
|
13
21
|
|
|
14
22
|
// --- macOS launchd ---
|
|
@@ -36,7 +44,10 @@ function buildPlist(): string {
|
|
|
36
44
|
<key>RunAtLoad</key>
|
|
37
45
|
<true/>
|
|
38
46
|
<key>KeepAlive</key>
|
|
39
|
-
<
|
|
47
|
+
<dict>
|
|
48
|
+
<key>SuccessfulExit</key>
|
|
49
|
+
<false/>
|
|
50
|
+
</dict>
|
|
40
51
|
<key>StandardOutPath</key>
|
|
41
52
|
<string>${paths.daemonLog}</string>
|
|
42
53
|
<key>StandardErrorPath</key>
|
package/src/core/daemon.ts
CHANGED
|
@@ -155,13 +155,27 @@ export async function runDaemon(): Promise<void> {
|
|
|
155
155
|
if (existingPid !== null && existingPid !== process.pid) {
|
|
156
156
|
try {
|
|
157
157
|
process.kill(existingPid, 0); // Check if alive
|
|
158
|
-
log.
|
|
159
|
-
process.exit(
|
|
158
|
+
log.debug({ existingPid, myPid: process.pid }, "another daemon is already running, exiting");
|
|
159
|
+
process.exit(0);
|
|
160
160
|
} catch {
|
|
161
161
|
// Dead PID in pidfile — safe to take over
|
|
162
162
|
}
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
+
// Crash handlers — ensure PID cleanup and logging on unhandled errors.
|
|
166
|
+
// Without these, an unhandled rejection kills the process silently,
|
|
167
|
+
// leaving a stale PID file that blocks restarts and hides the cause.
|
|
168
|
+
process.on("uncaughtException", (err) => {
|
|
169
|
+
log.fatal({ err }, "uncaught exception — cleaning up");
|
|
170
|
+
removePid();
|
|
171
|
+
process.exit(1);
|
|
172
|
+
});
|
|
173
|
+
process.on("unhandledRejection", (reason) => {
|
|
174
|
+
log.fatal({ reason }, "unhandled rejection — cleaning up");
|
|
175
|
+
removePid();
|
|
176
|
+
process.exit(1);
|
|
177
|
+
});
|
|
178
|
+
|
|
165
179
|
writePid(process.pid);
|
|
166
180
|
log.info({ pid: process.pid }, "daemon started");
|
|
167
181
|
|