@aman_asmuei/aman 0.1.0
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 +21 -0
- package/README.md +120 -0
- package/bin/aman.js +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +428 -0
- package/package.json +55 -0
- package/template/core-starter.md +47 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Aman Asmuei
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<br>
|
|
4
|
+
|
|
5
|
+
<picture>
|
|
6
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://img.shields.io/badge/aman-AI_companion-white?style=for-the-badge&labelColor=0d1117&color=58a6ff">
|
|
7
|
+
<img alt="aman" src="https://img.shields.io/badge/aman-AI_companion-black?style=for-the-badge&labelColor=f6f8fa&color=24292f">
|
|
8
|
+
</picture>
|
|
9
|
+
|
|
10
|
+
### Your complete AI companion.
|
|
11
|
+
|
|
12
|
+
Identity + Memory + Tools — one command, any AI.
|
|
13
|
+
|
|
14
|
+
<br>
|
|
15
|
+
|
|
16
|
+
[](https://www.npmjs.com/package/@aman_asmuei/aman)
|
|
17
|
+
[](LICENSE)
|
|
18
|
+
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## One Command
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npx @aman_asmuei/aman
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Sets up your complete AI ecosystem:
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
◆ aman — your complete AI companion
|
|
33
|
+
|
|
34
|
+
✔ Created ~/.acore/core.md (identity)
|
|
35
|
+
✔ Memory: run npx @aman_asmuei/amem to add automated memory
|
|
36
|
+
✔ Tools: run npx @aman_asmuei/akit add github to add capabilities
|
|
37
|
+
|
|
38
|
+
✔ Your AI companion is ready.
|
|
39
|
+
|
|
40
|
+
aman status See your full ecosystem
|
|
41
|
+
acore customize Change personality
|
|
42
|
+
akit add <tool> Add tools
|
|
43
|
+
npx @aman_asmuei/amem Enable automated memory
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## The Ecosystem
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
aman
|
|
52
|
+
├── acore → identity → who your AI IS
|
|
53
|
+
├── amem → memory → what your AI KNOWS
|
|
54
|
+
└── akit → tools → what your AI CAN DO
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
| Layer | Package | What it does |
|
|
58
|
+
|:------|:--------|:-------------|
|
|
59
|
+
| Identity | [acore](https://github.com/amanasmuei/acore) | Personality, values, relationship memory |
|
|
60
|
+
| Memory | [amem](https://github.com/amanasmuei/amem) | Automated knowledge storage (MCP) |
|
|
61
|
+
| Tools | [akit](https://github.com/amanasmuei/akit) | Portable AI capabilities (MCP + manual) |
|
|
62
|
+
| **Unified** | **aman** | **One command to set up everything** |
|
|
63
|
+
|
|
64
|
+
Each package works independently. `aman` is the front door.
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Commands
|
|
69
|
+
|
|
70
|
+
| Command | What it does |
|
|
71
|
+
|:--------|:------------|
|
|
72
|
+
| `aman` | First run: setup. After that: show status |
|
|
73
|
+
| `aman setup` | Set up identity + memory + tools |
|
|
74
|
+
| `aman status` | View your full ecosystem status |
|
|
75
|
+
|
|
76
|
+
After setup, use the individual CLIs for detailed management:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
acore customize # change AI personality
|
|
80
|
+
acore show # view identity
|
|
81
|
+
akit add github # add tools
|
|
82
|
+
akit search database # find tools
|
|
83
|
+
acore doctor # health check identity
|
|
84
|
+
akit doctor # health check tools
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Ecosystem Status
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
$ aman status
|
|
93
|
+
|
|
94
|
+
◆ aman — ecosystem status
|
|
95
|
+
|
|
96
|
+
✔ Identity: Companion + Aman
|
|
97
|
+
✔ Memory: amem connected (automated)
|
|
98
|
+
✔ Tools: 3 installed
|
|
99
|
+
✔ Platform: Claude Code (auto-save + MCP)
|
|
100
|
+
|
|
101
|
+
Ecosystem: Complete ecosystem (3/3 layers active)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Works With
|
|
107
|
+
|
|
108
|
+
ChatGPT, Claude, Claude Code, Cursor, Windsurf, Gemini, and any AI that accepts a system prompt.
|
|
109
|
+
|
|
110
|
+
## License
|
|
111
|
+
|
|
112
|
+
[MIT](LICENSE)
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
<div align="center">
|
|
117
|
+
|
|
118
|
+
**One command. Complete AI companion. Any platform.**
|
|
119
|
+
|
|
120
|
+
</div>
|
package/bin/aman.js
ADDED
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
|
|
4
|
+
// src/commands/setup.ts
|
|
5
|
+
import * as p from "@clack/prompts";
|
|
6
|
+
import pc from "picocolors";
|
|
7
|
+
import fs4 from "fs";
|
|
8
|
+
import path3 from "path";
|
|
9
|
+
import os2 from "os";
|
|
10
|
+
|
|
11
|
+
// src/lib/detect.ts
|
|
12
|
+
import fs from "fs";
|
|
13
|
+
import path from "path";
|
|
14
|
+
import os from "os";
|
|
15
|
+
import { execFileSync } from "child_process";
|
|
16
|
+
function detectUserName() {
|
|
17
|
+
try {
|
|
18
|
+
const name = execFileSync("git", ["config", "user.name"], {
|
|
19
|
+
encoding: "utf-8",
|
|
20
|
+
timeout: 3e3
|
|
21
|
+
}).trim();
|
|
22
|
+
return name || null;
|
|
23
|
+
} catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function detectPlatform(cwd) {
|
|
28
|
+
const dir = cwd ?? process.cwd();
|
|
29
|
+
const configPath = path.join(dir, ".acore", "config.json");
|
|
30
|
+
if (fs.existsSync(configPath)) {
|
|
31
|
+
try {
|
|
32
|
+
const raw = fs.readFileSync(configPath, "utf-8");
|
|
33
|
+
const parsed = JSON.parse(raw);
|
|
34
|
+
if (typeof parsed.platform === "string") {
|
|
35
|
+
const p3 = parsed.platform;
|
|
36
|
+
if (p3 === "claude-code" || p3 === "cursor" || p3 === "windsurf") return p3;
|
|
37
|
+
}
|
|
38
|
+
} catch {
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (fs.existsSync(path.join(dir, "CLAUDE.md"))) return "claude-code";
|
|
42
|
+
if (fs.existsSync(path.join(dir, ".cursorrules"))) return "cursor";
|
|
43
|
+
if (fs.existsSync(path.join(dir, ".windsurfrules"))) return "windsurf";
|
|
44
|
+
return "other";
|
|
45
|
+
}
|
|
46
|
+
function detectStack(cwd) {
|
|
47
|
+
const dir = cwd ?? process.cwd();
|
|
48
|
+
const parts = [];
|
|
49
|
+
const hasTs = fs.existsSync(path.join(dir, "tsconfig.json"));
|
|
50
|
+
if (hasTs) parts.push("TypeScript");
|
|
51
|
+
if (fs.existsSync(path.join(dir, "Cargo.toml"))) parts.push("Rust");
|
|
52
|
+
if (fs.existsSync(path.join(dir, "go.mod"))) parts.push("Go");
|
|
53
|
+
if (fs.existsSync(path.join(dir, "pyproject.toml")) || fs.existsSync(path.join(dir, "requirements.txt"))) parts.push("Python");
|
|
54
|
+
if (fs.existsSync(path.join(dir, "package.json"))) {
|
|
55
|
+
try {
|
|
56
|
+
const raw = fs.readFileSync(path.join(dir, "package.json"), "utf-8");
|
|
57
|
+
const pkg = JSON.parse(raw);
|
|
58
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
59
|
+
const frameworks = [["react", "React"], ["next", "Next.js"], ["vue", "Vue"], ["svelte", "Svelte"], ["express", "Express"]];
|
|
60
|
+
for (const [dep, label] of frameworks) {
|
|
61
|
+
if (dep in deps) {
|
|
62
|
+
if (!hasTs && !parts.includes("JavaScript")) parts.push("JavaScript");
|
|
63
|
+
parts.push(label);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
} catch {
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return parts.join(", ");
|
|
70
|
+
}
|
|
71
|
+
function detectRole(cwd) {
|
|
72
|
+
const dir = cwd ?? process.cwd();
|
|
73
|
+
const manifests = ["package.json", "tsconfig.json", "Cargo.toml", "go.mod", "pyproject.toml", "requirements.txt"];
|
|
74
|
+
return manifests.some((m) => fs.existsSync(path.join(dir, m))) ? "Developer" : "Professional";
|
|
75
|
+
}
|
|
76
|
+
function isMcpPlatform(platform) {
|
|
77
|
+
return platform === "claude-code" || platform === "cursor" || platform === "windsurf";
|
|
78
|
+
}
|
|
79
|
+
function detectEcosystem() {
|
|
80
|
+
const home = os.homedir();
|
|
81
|
+
const acorePath = path.join(home, ".acore", "core.md");
|
|
82
|
+
const akitPath = path.join(home, ".akit", "kit.md");
|
|
83
|
+
const akitInstalledPath = path.join(home, ".akit", "installed.json");
|
|
84
|
+
let akitToolCount = 0;
|
|
85
|
+
if (fs.existsSync(akitInstalledPath)) {
|
|
86
|
+
try {
|
|
87
|
+
const tools = JSON.parse(fs.readFileSync(akitInstalledPath, "utf-8"));
|
|
88
|
+
akitToolCount = Array.isArray(tools) ? tools.length : 0;
|
|
89
|
+
} catch {
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
let amemInstalled = false;
|
|
93
|
+
const mcpPaths = [
|
|
94
|
+
path.join(home, ".claude", "settings.json"),
|
|
95
|
+
path.join(process.cwd(), ".cursor", "mcp.json"),
|
|
96
|
+
path.join(home, ".windsurf", "mcp.json")
|
|
97
|
+
];
|
|
98
|
+
for (const mcpPath of mcpPaths) {
|
|
99
|
+
if (fs.existsSync(mcpPath)) {
|
|
100
|
+
try {
|
|
101
|
+
const raw = fs.readFileSync(mcpPath, "utf-8");
|
|
102
|
+
if (raw.includes("amem")) amemInstalled = true;
|
|
103
|
+
} catch {
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
acore: { installed: fs.existsSync(acorePath), path: acorePath },
|
|
109
|
+
amem: { installed: amemInstalled },
|
|
110
|
+
akit: { installed: fs.existsSync(akitPath), path: akitPath, toolCount: akitToolCount }
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// src/lib/template.ts
|
|
115
|
+
import fs2 from "fs";
|
|
116
|
+
import path2 from "path";
|
|
117
|
+
import { fileURLToPath } from "url";
|
|
118
|
+
var __dirname = path2.dirname(fileURLToPath(import.meta.url));
|
|
119
|
+
function getTemplatePath(name) {
|
|
120
|
+
const fromDist = path2.join(__dirname, "..", "template", `${name}.md`);
|
|
121
|
+
if (fs2.existsSync(fromDist)) return fromDist;
|
|
122
|
+
const fromSrc = path2.join(__dirname, "..", "..", "template", `${name}.md`);
|
|
123
|
+
return fromSrc;
|
|
124
|
+
}
|
|
125
|
+
function loadTemplate(name) {
|
|
126
|
+
return fs2.readFileSync(getTemplatePath(name), "utf-8");
|
|
127
|
+
}
|
|
128
|
+
function fillTemplate(template, values) {
|
|
129
|
+
let result = template;
|
|
130
|
+
for (const [key, value] of Object.entries(values)) {
|
|
131
|
+
result = result.replaceAll(`{{${key}}}`, value);
|
|
132
|
+
}
|
|
133
|
+
return result;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// src/lib/instructions.ts
|
|
137
|
+
var FILE_BASED_INSTRUCTIONS = `### Update Protocol
|
|
138
|
+
When user says "update core" OR when session is winding down:
|
|
139
|
+
1. Review conversation for new insights
|
|
140
|
+
2. Flag any Identity-level changes for explicit approval BEFORE proceeding
|
|
141
|
+
3. Read current ~/.acore/core.md
|
|
142
|
+
4. Write updated version directly to ~/.acore/core.md
|
|
143
|
+
5. Confirm what changed in 1-2 sentences
|
|
144
|
+
|
|
145
|
+
If file write fails for any reason, fall back to outputting the updated core.md in a code block.
|
|
146
|
+
|
|
147
|
+
### Update Permissions
|
|
148
|
+
- Auto-update (no approval needed): Session, Relationship.Work, Relationship.Learned patterns
|
|
149
|
+
- Approval required: Identity (any field), adding new sections
|
|
150
|
+
- Suggest only: structural changes to core.md
|
|
151
|
+
|
|
152
|
+
### Session Awareness
|
|
153
|
+
When the conversation is winding down:
|
|
154
|
+
- Proactively offer: "I noticed a few things this session. Want me to save them to your core?"
|
|
155
|
+
- If yes: follow Update Protocol (write directly)
|
|
156
|
+
- If no: respect it, don't ask again`;
|
|
157
|
+
var CLIPBOARD_INSTRUCTIONS = `### Update Protocol
|
|
158
|
+
When user says "update core" OR when session is winding down:
|
|
159
|
+
1. Review conversation for new insights
|
|
160
|
+
2. Flag any Identity-level changes for explicit approval
|
|
161
|
+
3. Output the complete updated core.md in a code block
|
|
162
|
+
4. User saves manually (paste into acore pull or replace file directly)
|
|
163
|
+
|
|
164
|
+
### Update Permissions
|
|
165
|
+
- Auto-update (no approval needed): Session, Relationship.Work, Relationship.Learned patterns
|
|
166
|
+
- Approval required: Identity (any field), adding new sections
|
|
167
|
+
- Suggest only: structural changes to core.md
|
|
168
|
+
|
|
169
|
+
### Session Awareness
|
|
170
|
+
When the conversation is winding down:
|
|
171
|
+
- Proactively offer: "Want me to generate an updated core.md with what I learned this session?"
|
|
172
|
+
- If yes: follow Update Protocol
|
|
173
|
+
- If no: respect it, don't ask again`;
|
|
174
|
+
function getUpdateInstructions(platform) {
|
|
175
|
+
if (platform === "claude-code" || platform === "cursor" || platform === "windsurf") {
|
|
176
|
+
return FILE_BASED_INSTRUCTIONS;
|
|
177
|
+
}
|
|
178
|
+
return CLIPBOARD_INSTRUCTIONS;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// src/lib/inject.ts
|
|
182
|
+
import fs3 from "fs";
|
|
183
|
+
var START_MARKER = "<!-- acore:start -->";
|
|
184
|
+
var END_MARKER = "<!-- acore:end -->";
|
|
185
|
+
function injectIntoFile(filePath, content) {
|
|
186
|
+
const wrapped = `${START_MARKER}
|
|
187
|
+
${content}
|
|
188
|
+
${END_MARKER}`;
|
|
189
|
+
if (!fs3.existsSync(filePath)) {
|
|
190
|
+
fs3.writeFileSync(filePath, wrapped + "\n", "utf-8");
|
|
191
|
+
return { created: true };
|
|
192
|
+
}
|
|
193
|
+
const existing = fs3.readFileSync(filePath, "utf-8");
|
|
194
|
+
const startIdx = existing.indexOf(START_MARKER);
|
|
195
|
+
const endIdx = existing.indexOf(END_MARKER);
|
|
196
|
+
if (startIdx !== -1 && endIdx !== -1) {
|
|
197
|
+
const before = existing.slice(0, startIdx);
|
|
198
|
+
const after = existing.slice(endIdx + END_MARKER.length);
|
|
199
|
+
fs3.writeFileSync(filePath, before + wrapped + after, "utf-8");
|
|
200
|
+
} else {
|
|
201
|
+
fs3.writeFileSync(filePath, existing + "\n" + wrapped + "\n", "utf-8");
|
|
202
|
+
}
|
|
203
|
+
return { created: false };
|
|
204
|
+
}
|
|
205
|
+
function getPlatformFile(platform) {
|
|
206
|
+
switch (platform) {
|
|
207
|
+
case "claude-code":
|
|
208
|
+
return "CLAUDE.md";
|
|
209
|
+
case "cursor":
|
|
210
|
+
return ".cursorrules";
|
|
211
|
+
case "windsurf":
|
|
212
|
+
return ".windsurfrules";
|
|
213
|
+
default:
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// src/commands/setup.ts
|
|
219
|
+
var ARCHETYPES = {
|
|
220
|
+
pragmatist: {
|
|
221
|
+
personality: "concise, practical, efficient",
|
|
222
|
+
communication: "lead with the answer, then explain if asked",
|
|
223
|
+
values: "simplicity over cleverness, shipping over perfection"
|
|
224
|
+
},
|
|
225
|
+
mentor: {
|
|
226
|
+
personality: "patient, thorough, encouraging",
|
|
227
|
+
communication: "explain step-by-step, celebrate progress",
|
|
228
|
+
values: "understanding over speed, safety over velocity"
|
|
229
|
+
},
|
|
230
|
+
"sparring-partner": {
|
|
231
|
+
personality: "direct, challenging, honest",
|
|
232
|
+
communication: "push back on weak ideas, ask hard questions",
|
|
233
|
+
values: "honesty over comfort, shipping over perfection"
|
|
234
|
+
},
|
|
235
|
+
collaborator: {
|
|
236
|
+
personality: "curious, supportive, adaptive",
|
|
237
|
+
communication: "explore ideas together, match your energy",
|
|
238
|
+
values: "understanding over speed"
|
|
239
|
+
},
|
|
240
|
+
architect: {
|
|
241
|
+
personality: "systematic, precise, forward-thinking",
|
|
242
|
+
communication: "plan before building, cover edge cases",
|
|
243
|
+
values: "safety over velocity, simplicity over cleverness"
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
async function setupCommand() {
|
|
247
|
+
p.intro(pc.bold("aman") + " \u2014 your complete AI companion");
|
|
248
|
+
const ecosystem = detectEcosystem();
|
|
249
|
+
if (ecosystem.acore.installed) {
|
|
250
|
+
p.log.success(`Identity: ${pc.dim("~/.acore/core.md")} already exists`);
|
|
251
|
+
} else {
|
|
252
|
+
p.log.step("Setting up your AI identity...");
|
|
253
|
+
let userName = detectUserName();
|
|
254
|
+
if (!userName) {
|
|
255
|
+
const prompted = await p.text({
|
|
256
|
+
message: "What's your name?",
|
|
257
|
+
validate: (v) => v.length === 0 ? "Name is required" : void 0
|
|
258
|
+
});
|
|
259
|
+
if (p.isCancel(prompted)) process.exit(0);
|
|
260
|
+
userName = prompted;
|
|
261
|
+
}
|
|
262
|
+
const platform = detectPlatform();
|
|
263
|
+
const stack = detectStack();
|
|
264
|
+
const userRole = detectRole();
|
|
265
|
+
const archetype = await p.select({
|
|
266
|
+
message: "Choose your AI's personality",
|
|
267
|
+
options: [
|
|
268
|
+
{ value: "collaborator", label: "Collaborator", hint: "curious, supportive, adaptive (recommended)" },
|
|
269
|
+
{ value: "pragmatist", label: "Pragmatist", hint: "concise, practical, efficient" },
|
|
270
|
+
{ value: "mentor", label: "Mentor", hint: "patient, thorough, encouraging" },
|
|
271
|
+
{ value: "sparring-partner", label: "Sparring Partner", hint: "direct, challenging, honest" },
|
|
272
|
+
{ value: "architect", label: "Architect", hint: "systematic, precise, forward-thinking" }
|
|
273
|
+
],
|
|
274
|
+
initialValue: "collaborator"
|
|
275
|
+
});
|
|
276
|
+
if (p.isCancel(archetype)) process.exit(0);
|
|
277
|
+
const arch = ARCHETYPES[archetype];
|
|
278
|
+
const aiName = await p.text({
|
|
279
|
+
message: "Name your AI companion",
|
|
280
|
+
placeholder: "Companion",
|
|
281
|
+
defaultValue: "Companion"
|
|
282
|
+
});
|
|
283
|
+
if (p.isCancel(aiName)) process.exit(0);
|
|
284
|
+
const template = loadTemplate("core-starter");
|
|
285
|
+
const content = fillTemplate(template, {
|
|
286
|
+
AI_NAME: aiName || "Companion",
|
|
287
|
+
USER_NAME: userName,
|
|
288
|
+
USER_ROLE: userRole,
|
|
289
|
+
PERSONALITY: arch.personality,
|
|
290
|
+
COMMUNICATION: arch.communication,
|
|
291
|
+
VALUES: arch.values,
|
|
292
|
+
BOUNDARIES: "won't pretend to be human, flags when out of depth",
|
|
293
|
+
DATE: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
|
|
294
|
+
UPDATE_INSTRUCTIONS: getUpdateInstructions(platform)
|
|
295
|
+
});
|
|
296
|
+
const globalDir = path3.join(os2.homedir(), ".acore");
|
|
297
|
+
fs4.mkdirSync(globalDir, { recursive: true });
|
|
298
|
+
fs4.writeFileSync(path3.join(globalDir, "core.md"), content, "utf-8");
|
|
299
|
+
p.log.success(`Created ${pc.dim("~/.acore/core.md")} (identity)`);
|
|
300
|
+
if (stack) {
|
|
301
|
+
const localDir = path3.join(process.cwd(), ".acore");
|
|
302
|
+
fs4.mkdirSync(localDir, { recursive: true });
|
|
303
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
304
|
+
const contextContent = `## Work
|
|
305
|
+
- Stack: ${stack}
|
|
306
|
+
- Domain:
|
|
307
|
+
- Focus:
|
|
308
|
+
|
|
309
|
+
## Session
|
|
310
|
+
- Last updated: ${today}
|
|
311
|
+
- Resume: [starting fresh]
|
|
312
|
+
- Active topics: []
|
|
313
|
+
- Recent decisions: []
|
|
314
|
+
- Temp notes: []
|
|
315
|
+
|
|
316
|
+
## Project Patterns
|
|
317
|
+
- [observations specific to this project \u2014 built over time]
|
|
318
|
+
`;
|
|
319
|
+
fs4.writeFileSync(path3.join(localDir, "context.md"), contextContent, "utf-8");
|
|
320
|
+
const configContent = JSON.stringify({ platform: platform || "other" }, null, 2) + "\n";
|
|
321
|
+
fs4.writeFileSync(path3.join(localDir, "config.json"), configContent, "utf-8");
|
|
322
|
+
}
|
|
323
|
+
const platformFile = getPlatformFile(platform);
|
|
324
|
+
if (platformFile) {
|
|
325
|
+
const globalContent = fs4.readFileSync(path3.join(globalDir, "core.md"), "utf-8");
|
|
326
|
+
const filePath = path3.join(process.cwd(), platformFile);
|
|
327
|
+
const result = injectIntoFile(filePath, globalContent);
|
|
328
|
+
if (result.created) {
|
|
329
|
+
p.log.success(`Created ${pc.dim(platformFile)} with identity`);
|
|
330
|
+
} else {
|
|
331
|
+
p.log.success(`Updated ${pc.dim(platformFile)} with identity`);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
const inferredParts = [userName, userRole];
|
|
335
|
+
if (stack) inferredParts.push(stack);
|
|
336
|
+
p.log.info(`Inferred: ${pc.dim(inferredParts.join(" \xB7 "))}`);
|
|
337
|
+
}
|
|
338
|
+
if (ecosystem.amem.installed) {
|
|
339
|
+
p.log.success("Memory: amem connected");
|
|
340
|
+
} else {
|
|
341
|
+
const platform = detectPlatform();
|
|
342
|
+
if (isMcpPlatform(platform)) {
|
|
343
|
+
p.log.info(`Memory: run ${pc.bold("npx @aman_asmuei/amem")} to add automated memory`);
|
|
344
|
+
} else {
|
|
345
|
+
p.log.info(`Memory: built-in via core.md (upgrade with ${pc.bold("npx @aman_asmuei/amem")})`);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
if (ecosystem.akit.installed) {
|
|
349
|
+
p.log.success(`Tools: ${ecosystem.akit.toolCount} tools configured`);
|
|
350
|
+
} else {
|
|
351
|
+
p.log.info(`Tools: run ${pc.bold("npx @aman_asmuei/akit add github")} to add capabilities`);
|
|
352
|
+
}
|
|
353
|
+
const card = [
|
|
354
|
+
"",
|
|
355
|
+
` ${pc.green("\u2714")} Your AI companion is ready.`,
|
|
356
|
+
"",
|
|
357
|
+
` ${pc.bold("aman status")} See your full ecosystem`,
|
|
358
|
+
` ${pc.bold("acore customize")} Change personality`,
|
|
359
|
+
` ${pc.bold("akit add <tool>")} Add tools`,
|
|
360
|
+
` ${pc.bold("npx @aman_asmuei/amem")} Enable automated memory`,
|
|
361
|
+
""
|
|
362
|
+
];
|
|
363
|
+
p.note(card.join("\n"), "");
|
|
364
|
+
p.outro(pc.dim("Identity + Memory + Tools \u2014 one ecosystem."));
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// src/commands/status.ts
|
|
368
|
+
import * as p2 from "@clack/prompts";
|
|
369
|
+
import pc2 from "picocolors";
|
|
370
|
+
import fs5 from "fs";
|
|
371
|
+
function statusCommand() {
|
|
372
|
+
p2.intro(pc2.bold("aman") + " \u2014 ecosystem status");
|
|
373
|
+
const ecosystem = detectEcosystem();
|
|
374
|
+
const platform = detectPlatform();
|
|
375
|
+
if (ecosystem.acore.installed) {
|
|
376
|
+
const content = fs5.readFileSync(ecosystem.acore.path, "utf-8");
|
|
377
|
+
const aiName = content.match(/^# (.+)$/m)?.[1] || "Companion";
|
|
378
|
+
const userName = content.match(/- Name: (.+)$/m)?.[1] || "Unknown";
|
|
379
|
+
p2.log.success(`Identity: ${pc2.bold(aiName)} + ${pc2.bold(userName)}`);
|
|
380
|
+
} else {
|
|
381
|
+
p2.log.error(`Identity: not set up \u2014 run ${pc2.bold("aman setup")}`);
|
|
382
|
+
}
|
|
383
|
+
if (ecosystem.amem.installed) {
|
|
384
|
+
p2.log.success("Memory: amem connected (automated)");
|
|
385
|
+
} else {
|
|
386
|
+
p2.log.warning(`Memory: standalone mode \u2014 ${pc2.dim("npx @aman_asmuei/amem to upgrade")}`);
|
|
387
|
+
}
|
|
388
|
+
if (ecosystem.akit.installed) {
|
|
389
|
+
p2.log.success(`Tools: ${ecosystem.akit.toolCount} installed \u2014 ${pc2.dim("akit list for details")}`);
|
|
390
|
+
} else {
|
|
391
|
+
p2.log.warning(`Tools: none \u2014 ${pc2.dim("npx @aman_asmuei/akit add github")}`);
|
|
392
|
+
}
|
|
393
|
+
const platformLabels = {
|
|
394
|
+
"claude-code": "Claude Code",
|
|
395
|
+
cursor: "Cursor",
|
|
396
|
+
windsurf: "Windsurf",
|
|
397
|
+
other: "Manual"
|
|
398
|
+
};
|
|
399
|
+
const platLabel = platformLabels[platform] || platform;
|
|
400
|
+
if (isMcpPlatform(platform)) {
|
|
401
|
+
p2.log.success(`Platform: ${pc2.bold(platLabel)} (auto-save + MCP)`);
|
|
402
|
+
} else {
|
|
403
|
+
p2.log.info(`Platform: ${pc2.bold(platLabel)}`);
|
|
404
|
+
}
|
|
405
|
+
let score = 0;
|
|
406
|
+
if (ecosystem.acore.installed) score += 1;
|
|
407
|
+
if (ecosystem.amem.installed) score += 1;
|
|
408
|
+
if (ecosystem.akit.installed) score += 1;
|
|
409
|
+
const levels = ["Not started", "Getting started", "Growing", "Complete ecosystem"];
|
|
410
|
+
const colors = [pc2.red, pc2.yellow, pc2.cyan, pc2.green];
|
|
411
|
+
p2.log.message("");
|
|
412
|
+
p2.log.info(`Ecosystem: ${colors[score](levels[score])} (${score}/3 layers active)`);
|
|
413
|
+
p2.outro("");
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// src/index.ts
|
|
417
|
+
var program = new Command();
|
|
418
|
+
program.name("aman").description("Your complete AI companion \u2014 identity, memory, and tools in one command").version("0.1.0").action(() => {
|
|
419
|
+
const ecosystem = detectEcosystem();
|
|
420
|
+
if (ecosystem.acore.installed) {
|
|
421
|
+
statusCommand();
|
|
422
|
+
} else {
|
|
423
|
+
setupCommand();
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
program.command("setup").description("Set up your AI companion (identity + memory + tools)").action(() => setupCommand());
|
|
427
|
+
program.command("status").description("View your full ecosystem status").action(() => statusCommand());
|
|
428
|
+
program.parse();
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aman_asmuei/aman",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Your complete AI companion — identity, memory, and tools in one command",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"aman": "./bin/aman.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"bin",
|
|
13
|
+
"template"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsup",
|
|
17
|
+
"dev": "tsup --watch",
|
|
18
|
+
"test": "vitest run",
|
|
19
|
+
"test:watch": "vitest",
|
|
20
|
+
"lint": "tsc --noEmit",
|
|
21
|
+
"prepublishOnly": "npm run build"
|
|
22
|
+
},
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": ">=18"
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"ai",
|
|
28
|
+
"llm",
|
|
29
|
+
"identity",
|
|
30
|
+
"memory",
|
|
31
|
+
"tools",
|
|
32
|
+
"agent",
|
|
33
|
+
"cli",
|
|
34
|
+
"acore",
|
|
35
|
+
"amem",
|
|
36
|
+
"akit"
|
|
37
|
+
],
|
|
38
|
+
"author": "Aman Asmuei",
|
|
39
|
+
"license": "MIT",
|
|
40
|
+
"repository": {
|
|
41
|
+
"type": "git",
|
|
42
|
+
"url": "https://github.com/amanasmuei/aman.git"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"@clack/prompts": "^0.9.1",
|
|
46
|
+
"commander": "^13.1.0",
|
|
47
|
+
"picocolors": "^1.1.1"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@types/node": "^22.0.0",
|
|
51
|
+
"tsup": "^8.4.0",
|
|
52
|
+
"typescript": "^5.7.0",
|
|
53
|
+
"vitest": "^3.0.0"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# {{AI_NAME}}
|
|
2
|
+
|
|
3
|
+
## Identity
|
|
4
|
+
- Role: {{AI_NAME}} is {{USER_NAME}}'s personal AI partner ({{USER_ROLE}})
|
|
5
|
+
- Personality: {{PERSONALITY}}
|
|
6
|
+
- Communication: {{COMMUNICATION}}
|
|
7
|
+
- Values: {{VALUES}}
|
|
8
|
+
- Boundaries: {{BOUNDARIES}}
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Relationship
|
|
13
|
+
- Name: {{USER_NAME}}
|
|
14
|
+
- Role: {{USER_ROLE}}
|
|
15
|
+
- Communication: [learns over time]
|
|
16
|
+
- Work: [set per project]
|
|
17
|
+
- Learned patterns: [builds over time]
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Session
|
|
22
|
+
- Last updated: {{DATE}}
|
|
23
|
+
- Resume: [first session]
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Instructions
|
|
28
|
+
|
|
29
|
+
{{UPDATE_INSTRUCTIONS}}
|
|
30
|
+
|
|
31
|
+
### Growth Protocol
|
|
32
|
+
After 3 sessions: suggest adding Dynamics (trust, emotional patterns, conflict history)
|
|
33
|
+
After 5 sessions: suggest adding Context Modes (coding, creative, personal behavior)
|
|
34
|
+
After 10 sessions: suggest adding Memory Lifecycle rules (consolidation, compression, forgetting)
|
|
35
|
+
Each addition requires explicit user approval.
|
|
36
|
+
|
|
37
|
+
### amem Integration (full mode)
|
|
38
|
+
When amem MCP tools are available:
|
|
39
|
+
- Session start: call `memory_recall` to enrich context
|
|
40
|
+
- During session: call `memory_store` for significant observations
|
|
41
|
+
- Session end: call `memory_context` for consolidation candidates
|
|
42
|
+
- Overflow: move details to amem, keep `[→ amem: topic]` pointer
|
|
43
|
+
|
|
44
|
+
### Without amem (standalone mode)
|
|
45
|
+
- All updates via "update core" command or automatic file write
|
|
46
|
+
- AI follows Memory Lifecycle rules when generating updates
|
|
47
|
+
- AI reminds user to update if 3+ sessions have passed
|