@newtype-ai/nit 0.2.3 → 0.2.5
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 +15 -9
- package/dist/{chunk-2ZHTOY2J.js → chunk-U6PEH4IJ.js} +119 -19
- package/dist/cli.js +6 -2
- package/dist/index.d.ts +9 -3
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -57,7 +57,7 @@ nit push --all
|
|
|
57
57
|
| `nit checkout <branch>` | Switch branch (overwrites agent-card.json) |
|
|
58
58
|
| `nit push [--all]` | Push branch(es) to remote |
|
|
59
59
|
| `nit sign "msg"` | Sign a message with your Ed25519 key |
|
|
60
|
-
| `nit sign --login <domain>` |
|
|
60
|
+
| `nit sign --login <domain>` | Auto-switch to domain branch + generate login payload |
|
|
61
61
|
| `nit remote` | Show remote URL and credential status |
|
|
62
62
|
| `nit remote add <name> <url>` | Add a new remote |
|
|
63
63
|
| `nit remote set-url <name> <url>` | Change a remote's URL |
|
|
@@ -84,7 +84,9 @@ Each branch is a different agent card for a different platform. Branch name = ro
|
|
|
84
84
|
|
|
85
85
|
### Skill Resolution
|
|
86
86
|
|
|
87
|
-
|
|
87
|
+
Your card can store skills as **pointers** — just `{ "id": "skill-name" }` — resolved from SKILL.md files at commit time. SKILL.md is the single source of truth when present.
|
|
88
|
+
|
|
89
|
+
nit auto-discovers your skills directory from all major agent frameworks:
|
|
88
90
|
|
|
89
91
|
- `.claude/skills/` — Claude Code
|
|
90
92
|
- `.cursor/skills/` — Cursor
|
|
@@ -92,7 +94,7 @@ At commit time, nit discovers SKILL.md files from all major agent frameworks:
|
|
|
92
94
|
- `.codex/skills/` — OpenAI Codex
|
|
93
95
|
- `.agents/skills/` — Generic
|
|
94
96
|
|
|
95
|
-
|
|
97
|
+
The discovered path is stored in `.nit/config`. When `nit sign --login <domain>` creates a new branch, it auto-creates a SKILL.md template and adds a pointer to the card. The committed card always contains fully resolved, self-contained skill data.
|
|
96
98
|
|
|
97
99
|
### Remote Protocol
|
|
98
100
|
|
|
@@ -116,10 +118,11 @@ nit remote set-url origin https://my-server.com
|
|
|
116
118
|
your-project/
|
|
117
119
|
├── .nit/ # nit repository (gitignored)
|
|
118
120
|
│ ├── HEAD # Current branch ref
|
|
119
|
-
│ ├── config # Remote URL
|
|
121
|
+
│ ├── config # Remote URL, credentials, skills directory
|
|
120
122
|
│ ├── identity/
|
|
121
123
|
│ │ ├── agent.pub # Ed25519 public key
|
|
122
|
-
│ │
|
|
124
|
+
│ │ ├── agent.key # Ed25519 private key (0600)
|
|
125
|
+
│ │ └── agent-id # UUIDv5 derived from public key
|
|
123
126
|
│ ├── objects/ # Content-addressable store
|
|
124
127
|
│ └── refs/heads/ # Branch pointers
|
|
125
128
|
├── agent-card.json # Working copy (changes with checkout)
|
|
@@ -129,12 +132,15 @@ your-project/
|
|
|
129
132
|
## Programmatic API
|
|
130
133
|
|
|
131
134
|
```typescript
|
|
132
|
-
import { init, commit, checkout, branch, push, status } from '@newtype-ai/nit';
|
|
135
|
+
import { init, commit, checkout, branch, push, status, sign, loginPayload } from '@newtype-ai/nit';
|
|
133
136
|
|
|
134
137
|
await init();
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
+
|
|
139
|
+
// Log into an app (auto-creates and switches to domain branch)
|
|
140
|
+
const payload = await loginPayload('faam.io');
|
|
141
|
+
// → { agent_id, domain, timestamp, signature, switchedBranch, createdSkill }
|
|
142
|
+
|
|
143
|
+
// Customize card, then commit & push
|
|
138
144
|
await commit('FAAM config');
|
|
139
145
|
await push({ all: true });
|
|
140
146
|
```
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { promises as fs6, statSync } from "fs";
|
|
5
|
-
import { join as join6, basename as basename2, resolve } from "path";
|
|
5
|
+
import { join as join6, basename as basename2, resolve as resolve2 } from "path";
|
|
6
6
|
|
|
7
7
|
// src/objects.ts
|
|
8
8
|
import { createHash } from "crypto";
|
|
@@ -312,8 +312,70 @@ function uuidv5(name, namespace) {
|
|
|
312
312
|
|
|
313
313
|
// src/skills.ts
|
|
314
314
|
import { promises as fs4 } from "fs";
|
|
315
|
-
import { join as join4 } from "path";
|
|
315
|
+
import { join as join4, resolve } from "path";
|
|
316
316
|
import { homedir } from "os";
|
|
317
|
+
var FRAMEWORK_MARKERS = [
|
|
318
|
+
{ marker: ".claude", skillsPath: ".claude/skills" },
|
|
319
|
+
{ marker: ".cursor", skillsPath: ".cursor/skills" },
|
|
320
|
+
{ marker: ".codex", skillsPath: ".codex/skills" },
|
|
321
|
+
{ marker: ".windsurf", skillsPath: ".windsurf/skills" },
|
|
322
|
+
{ marker: ".openclaw", skillsPath: ".agents/skills" }
|
|
323
|
+
];
|
|
324
|
+
var GLOBAL_SKILLS_DIRS = [
|
|
325
|
+
{ marker: ".claude", skillsPath: ".claude/skills" },
|
|
326
|
+
{ marker: ".codex", skillsPath: ".codex/skills" },
|
|
327
|
+
{ marker: ".codeium", skillsPath: ".codeium/windsurf/skills" }
|
|
328
|
+
];
|
|
329
|
+
async function discoverSkillsDir(projectDir2) {
|
|
330
|
+
const absProject = resolve(projectDir2);
|
|
331
|
+
const home = homedir();
|
|
332
|
+
for (const { marker, skillsPath } of FRAMEWORK_MARKERS) {
|
|
333
|
+
if (absProject.includes(`/${marker}/`) || absProject.includes(`/${marker}`)) {
|
|
334
|
+
return join4(absProject, skillsPath);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
for (const { marker, skillsPath } of FRAMEWORK_MARKERS) {
|
|
338
|
+
try {
|
|
339
|
+
const stat = await fs4.stat(join4(absProject, marker));
|
|
340
|
+
if (stat.isDirectory()) {
|
|
341
|
+
return join4(absProject, skillsPath);
|
|
342
|
+
}
|
|
343
|
+
} catch {
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
for (const { marker, skillsPath } of GLOBAL_SKILLS_DIRS) {
|
|
347
|
+
try {
|
|
348
|
+
const stat = await fs4.stat(join4(home, marker));
|
|
349
|
+
if (stat.isDirectory()) {
|
|
350
|
+
return join4(home, skillsPath);
|
|
351
|
+
}
|
|
352
|
+
} catch {
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
return join4(absProject, ".agents", "skills");
|
|
356
|
+
}
|
|
357
|
+
async function createSkillTemplate(skillsDir, domain) {
|
|
358
|
+
const skillId = domain.replace(/\./g, "-");
|
|
359
|
+
const skillDir = join4(skillsDir, skillId);
|
|
360
|
+
const skillPath = join4(skillDir, "SKILL.md");
|
|
361
|
+
try {
|
|
362
|
+
await fs4.access(skillPath);
|
|
363
|
+
return skillId;
|
|
364
|
+
} catch {
|
|
365
|
+
}
|
|
366
|
+
await fs4.mkdir(skillDir, { recursive: true });
|
|
367
|
+
const template = `---
|
|
368
|
+
name: ${skillId}
|
|
369
|
+
description: Skills and context for ${domain}
|
|
370
|
+
---
|
|
371
|
+
|
|
372
|
+
# ${skillId}
|
|
373
|
+
|
|
374
|
+
Configure your skills and context for ${domain} here.
|
|
375
|
+
`;
|
|
376
|
+
await fs4.writeFile(skillPath, template, "utf-8");
|
|
377
|
+
return skillId;
|
|
378
|
+
}
|
|
317
379
|
async function discoverSkills(projectDir2) {
|
|
318
380
|
const home = homedir();
|
|
319
381
|
const searchDirs = [
|
|
@@ -624,33 +686,49 @@ async function getRemoteUrl(nitDir, remoteName) {
|
|
|
624
686
|
const config = await readConfig(nitDir);
|
|
625
687
|
return config.remotes[remoteName]?.url ?? null;
|
|
626
688
|
}
|
|
689
|
+
async function getSkillsDir(nitDir) {
|
|
690
|
+
const config = await readConfig(nitDir);
|
|
691
|
+
return config.skillsDir ?? null;
|
|
692
|
+
}
|
|
627
693
|
function parseConfig(raw) {
|
|
628
694
|
const remotes = {};
|
|
695
|
+
let currentSection = null;
|
|
629
696
|
let currentRemote = null;
|
|
697
|
+
let skillsDir;
|
|
630
698
|
for (const line of raw.split("\n")) {
|
|
631
699
|
const trimmed = line.trim();
|
|
632
700
|
if (trimmed === "" || trimmed.startsWith("#")) continue;
|
|
633
|
-
const
|
|
634
|
-
if (
|
|
635
|
-
|
|
701
|
+
const remoteMatch = trimmed.match(/^\[remote\s+"([^"]+)"\]$/);
|
|
702
|
+
if (remoteMatch) {
|
|
703
|
+
currentSection = "remote";
|
|
704
|
+
currentRemote = remoteMatch[1];
|
|
636
705
|
if (!remotes[currentRemote]) {
|
|
637
706
|
remotes[currentRemote] = {};
|
|
638
707
|
}
|
|
639
708
|
continue;
|
|
640
709
|
}
|
|
641
|
-
if (
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
710
|
+
if (trimmed === "[skills]") {
|
|
711
|
+
currentSection = "skills";
|
|
712
|
+
currentRemote = null;
|
|
713
|
+
continue;
|
|
714
|
+
}
|
|
715
|
+
const kvMatch = trimmed.match(/^(\w+)\s*=\s*(.+)$/);
|
|
716
|
+
if (kvMatch) {
|
|
717
|
+
const [, key, value] = kvMatch;
|
|
718
|
+
if (currentSection === "remote" && currentRemote !== null) {
|
|
645
719
|
if (key === "url") {
|
|
646
720
|
remotes[currentRemote].url = value.trim();
|
|
647
721
|
} else if (key === "credential") {
|
|
648
722
|
remotes[currentRemote].credential = value.trim();
|
|
649
723
|
}
|
|
724
|
+
} else if (currentSection === "skills") {
|
|
725
|
+
if (key === "dir") {
|
|
726
|
+
skillsDir = value.trim();
|
|
727
|
+
}
|
|
650
728
|
}
|
|
651
729
|
}
|
|
652
730
|
}
|
|
653
|
-
return { remotes };
|
|
731
|
+
return { remotes, skillsDir };
|
|
654
732
|
}
|
|
655
733
|
function serializeConfig(config) {
|
|
656
734
|
const lines = [];
|
|
@@ -664,6 +742,11 @@ function serializeConfig(config) {
|
|
|
664
742
|
}
|
|
665
743
|
lines.push("");
|
|
666
744
|
}
|
|
745
|
+
if (config.skillsDir) {
|
|
746
|
+
lines.push("[skills]");
|
|
747
|
+
lines.push(` dir = ${config.skillsDir}`);
|
|
748
|
+
lines.push("");
|
|
749
|
+
}
|
|
667
750
|
return lines.join("\n");
|
|
668
751
|
}
|
|
669
752
|
|
|
@@ -672,7 +755,7 @@ var NIT_DIR = ".nit";
|
|
|
672
755
|
var CARD_FILE = "agent-card.json";
|
|
673
756
|
var DEFAULT_API_BASE = "https://api.newtype-ai.org";
|
|
674
757
|
function findNitDir(startDir) {
|
|
675
|
-
let dir =
|
|
758
|
+
let dir = resolve2(startDir || process.cwd());
|
|
676
759
|
while (true) {
|
|
677
760
|
const candidate = join6(dir, NIT_DIR);
|
|
678
761
|
try {
|
|
@@ -680,7 +763,7 @@ function findNitDir(startDir) {
|
|
|
680
763
|
if (s.isDirectory()) return candidate;
|
|
681
764
|
} catch {
|
|
682
765
|
}
|
|
683
|
-
const parent =
|
|
766
|
+
const parent = resolve2(dir, "..");
|
|
684
767
|
if (parent === dir) {
|
|
685
768
|
throw new Error(
|
|
686
769
|
"Not a nit repository (or any parent directory). Run `nit init` first."
|
|
@@ -690,7 +773,7 @@ function findNitDir(startDir) {
|
|
|
690
773
|
}
|
|
691
774
|
}
|
|
692
775
|
function projectDir(nitDir) {
|
|
693
|
-
return
|
|
776
|
+
return resolve2(nitDir, "..");
|
|
694
777
|
}
|
|
695
778
|
async function readWorkingCard(nitDir) {
|
|
696
779
|
const cardPath = join6(projectDir(nitDir), CARD_FILE);
|
|
@@ -720,7 +803,7 @@ async function getAuthorName(nitDir) {
|
|
|
720
803
|
}
|
|
721
804
|
}
|
|
722
805
|
async function init(options) {
|
|
723
|
-
const projDir =
|
|
806
|
+
const projDir = resolve2(options?.projectDir || process.cwd());
|
|
724
807
|
const nitDir = join6(projDir, NIT_DIR);
|
|
725
808
|
try {
|
|
726
809
|
await fs6.access(nitDir);
|
|
@@ -781,14 +864,17 @@ async function init(options) {
|
|
|
781
864
|
await setBranch(nitDir, "main", commitHash);
|
|
782
865
|
await setHead(nitDir, "main");
|
|
783
866
|
await fs6.writeFile(join6(nitDir, "logs", "HEAD"), "", "utf-8");
|
|
867
|
+
const skillsDir = await discoverSkillsDir(projDir);
|
|
784
868
|
await writeConfig(nitDir, {
|
|
785
|
-
remotes: { origin: { url: DEFAULT_API_BASE } }
|
|
869
|
+
remotes: { origin: { url: DEFAULT_API_BASE } },
|
|
870
|
+
skillsDir
|
|
786
871
|
});
|
|
787
872
|
return {
|
|
788
873
|
agentId,
|
|
789
874
|
publicKey: publicKeyField,
|
|
790
875
|
cardUrl: card.url,
|
|
791
|
-
skillsFound
|
|
876
|
+
skillsFound,
|
|
877
|
+
skillsDir
|
|
792
878
|
};
|
|
793
879
|
}
|
|
794
880
|
async function status(options) {
|
|
@@ -858,15 +944,29 @@ async function sign2(message, options) {
|
|
|
858
944
|
async function loginPayload(domain, options) {
|
|
859
945
|
const nitDir = findNitDir(options?.projectDir);
|
|
860
946
|
let switchedBranch;
|
|
947
|
+
let createdSkill;
|
|
861
948
|
const currentBranch = await getCurrentBranch(nitDir);
|
|
862
949
|
if (currentBranch !== domain) {
|
|
863
|
-
const
|
|
864
|
-
if (
|
|
950
|
+
const isNew = !await getBranch(nitDir, domain);
|
|
951
|
+
if (isNew) {
|
|
865
952
|
const headHash = await resolveHead(nitDir);
|
|
866
953
|
await setBranch(nitDir, domain, headHash);
|
|
867
954
|
}
|
|
868
955
|
await checkout(domain, options);
|
|
869
956
|
switchedBranch = domain;
|
|
957
|
+
if (isNew) {
|
|
958
|
+
const skillsDir = await getSkillsDir(nitDir);
|
|
959
|
+
if (skillsDir) {
|
|
960
|
+
const skillId = await createSkillTemplate(skillsDir, domain);
|
|
961
|
+
const card = await readWorkingCard(nitDir);
|
|
962
|
+
const hasSkill = card.skills.some((s) => s.id === skillId);
|
|
963
|
+
if (!hasSkill) {
|
|
964
|
+
card.skills.push({ id: skillId });
|
|
965
|
+
await writeWorkingCard(nitDir, card);
|
|
966
|
+
createdSkill = skillId;
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
}
|
|
870
970
|
}
|
|
871
971
|
const agentId = await loadAgentId(nitDir);
|
|
872
972
|
const timestamp = Math.floor(Date.now() / 1e3);
|
|
@@ -874,7 +974,7 @@ async function loginPayload(domain, options) {
|
|
|
874
974
|
${domain}
|
|
875
975
|
${timestamp}`;
|
|
876
976
|
const signature = await signMessage(nitDir, message);
|
|
877
|
-
return { agent_id: agentId, domain, timestamp, signature, switchedBranch };
|
|
977
|
+
return { agent_id: agentId, domain, timestamp, signature, switchedBranch, createdSkill };
|
|
878
978
|
}
|
|
879
979
|
async function commit(message, options) {
|
|
880
980
|
const nitDir = findNitDir(options?.projectDir);
|
package/dist/cli.js
CHANGED
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
remoteSetUrl,
|
|
16
16
|
sign,
|
|
17
17
|
status
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-U6PEH4IJ.js";
|
|
19
19
|
|
|
20
20
|
// src/cli.ts
|
|
21
21
|
var bold = (s) => `\x1B[1m${s}\x1B[0m`;
|
|
@@ -80,6 +80,7 @@ async function cmdInit() {
|
|
|
80
80
|
console.log(` Agent ID: ${green(result.agentId)}`);
|
|
81
81
|
console.log(` Public key: ${dim(result.publicKey)}`);
|
|
82
82
|
console.log(` Card URL: ${result.cardUrl}`);
|
|
83
|
+
console.log(` Skills dir: ${dim(result.skillsDir)}`);
|
|
83
84
|
if (result.skillsFound.length > 0) {
|
|
84
85
|
console.log(` Skills: ${result.skillsFound.join(", ")}`);
|
|
85
86
|
} else {
|
|
@@ -233,7 +234,10 @@ async function cmdSign(args) {
|
|
|
233
234
|
if (payload.switchedBranch) {
|
|
234
235
|
console.error(`Switched to branch '${payload.switchedBranch}'`);
|
|
235
236
|
}
|
|
236
|
-
|
|
237
|
+
if (payload.createdSkill) {
|
|
238
|
+
console.error(`Created skill template '${payload.createdSkill}'`);
|
|
239
|
+
}
|
|
240
|
+
const { switchedBranch: _s, createdSkill: _c, ...output } = payload;
|
|
237
241
|
console.log(JSON.stringify(output, null, 2));
|
|
238
242
|
return;
|
|
239
243
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -36,6 +36,8 @@ interface NitRemoteConfig {
|
|
|
36
36
|
interface NitConfig {
|
|
37
37
|
/** Keyed by remote name (e.g. "origin") */
|
|
38
38
|
remotes: Record<string, NitRemoteConfig>;
|
|
39
|
+
/** Discovered skills directory path */
|
|
40
|
+
skillsDir?: string;
|
|
39
41
|
}
|
|
40
42
|
/** A2A-compatible agent card. */
|
|
41
43
|
interface AgentCard {
|
|
@@ -56,11 +58,13 @@ interface AgentCard {
|
|
|
56
58
|
url?: string;
|
|
57
59
|
};
|
|
58
60
|
}
|
|
59
|
-
/** A single skill entry in an agent card.
|
|
61
|
+
/** A single skill entry in an agent card.
|
|
62
|
+
* Can be a full skill (all fields) or a pointer (just id).
|
|
63
|
+
* At commit time, pointers are resolved from SKILL.md files. */
|
|
60
64
|
interface AgentCardSkill {
|
|
61
65
|
id: string;
|
|
62
|
-
name
|
|
63
|
-
description
|
|
66
|
+
name?: string;
|
|
67
|
+
description?: string;
|
|
64
68
|
tags?: string[];
|
|
65
69
|
examples?: string[];
|
|
66
70
|
inputModes?: string[];
|
|
@@ -200,6 +204,7 @@ interface InitResult {
|
|
|
200
204
|
publicKey: string;
|
|
201
205
|
cardUrl: string;
|
|
202
206
|
skillsFound: string[];
|
|
207
|
+
skillsDir: string;
|
|
203
208
|
}
|
|
204
209
|
/**
|
|
205
210
|
* Initialize a new nit repository in the project directory.
|
|
@@ -235,6 +240,7 @@ declare function loginPayload(domain: string, options?: {
|
|
|
235
240
|
projectDir?: string;
|
|
236
241
|
}): Promise<LoginPayload & {
|
|
237
242
|
switchedBranch?: string;
|
|
243
|
+
createdSkill?: string;
|
|
238
244
|
}>;
|
|
239
245
|
/**
|
|
240
246
|
* Snapshot the current agent-card.json as a new commit.
|
package/dist/index.js
CHANGED