@newtype-ai/nit 0.2.2 → 0.2.4

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 CHANGED
@@ -59,6 +59,8 @@ nit push --all
59
59
  | `nit sign "msg"` | Sign a message with your Ed25519 key |
60
60
  | `nit sign --login <domain>` | Generate login payload for an app |
61
61
  | `nit remote` | Show remote URL and credential status |
62
+ | `nit remote add <name> <url>` | Add a new remote |
63
+ | `nit remote set-url <name> <url>` | Change a remote's URL |
62
64
 
63
65
  ## How It Works
64
66
 
@@ -102,7 +104,11 @@ GET /.well-known/agent-card.json?branch=faam.io → 401 { challenge }
102
104
  GET ... + X-Nit-Signature + X-Nit-Challenge → branch card
103
105
  ```
104
106
 
105
- nit is the client. Any server can implement the protocol. [newtype-ai.org](https://newtype-ai.org) provides a hosted implementation.
107
+ nit is the client. Any server can implement the protocol. [newtype-ai.org](https://newtype-ai.org) is the recommended free hosting service, but you can point to any compatible server:
108
+
109
+ ```bash
110
+ nit remote set-url origin https://my-server.com
111
+ ```
106
112
 
107
113
  ## Directory Structure
108
114
 
@@ -110,7 +116,7 @@ nit is the client. Any server can implement the protocol. [newtype-ai.org](https
110
116
  your-project/
111
117
  ├── .nit/ # nit repository (gitignored)
112
118
  │ ├── HEAD # Current branch ref
113
- │ ├── config # Remote credentials
119
+ │ ├── config # Remote URL and credentials
114
120
  │ ├── identity/
115
121
  │ │ ├── agent.pub # Ed25519 public key
116
122
  │ │ └── agent.key # Ed25519 private key (0600)
@@ -1,8 +1,8 @@
1
1
  // nit — version control for agent cards
2
2
 
3
3
  // src/index.ts
4
- import { promises as fs5, statSync } from "fs";
5
- import { join as join5, basename as basename2, resolve } from "path";
4
+ import { promises as fs6, statSync } from "fs";
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 = [
@@ -512,7 +574,6 @@ function formatValue(val) {
512
574
 
513
575
  // src/remote.ts
514
576
  import { createHash as createHash3 } from "crypto";
515
- var API_BASE = "https://api.newtype-ai.org";
516
577
  function sha256Hex(data) {
517
578
  return createHash3("sha256").update(data, "utf-8").digest("hex");
518
579
  }
@@ -534,12 +595,12 @@ ${sha256Hex(body)}`;
534
595
  "X-Nit-Signature": signature
535
596
  };
536
597
  }
537
- async function pushBranch(nitDir, remoteName, branch2, cardJson, commitHash) {
598
+ async function pushBranch(nitDir, apiBase, branch2, cardJson, commitHash) {
538
599
  const path = `/agent-card/branches/${encodeURIComponent(branch2)}`;
539
600
  const body = JSON.stringify({ card_json: cardJson, commit_hash: commitHash });
540
601
  try {
541
602
  const authHeaders = await buildAuthHeaders(nitDir, "PUT", path, body);
542
- const res = await fetch(`${API_BASE}${path}`, {
603
+ const res = await fetch(`${apiBase}${path}`, {
543
604
  method: "PUT",
544
605
  headers: {
545
606
  "Content-Type": "application/json",
@@ -552,17 +613,17 @@ async function pushBranch(nitDir, remoteName, branch2, cardJson, commitHash) {
552
613
  return {
553
614
  branch: branch2,
554
615
  commitHash,
555
- remoteUrl: API_BASE,
616
+ remoteUrl: apiBase,
556
617
  success: false,
557
618
  error: `HTTP ${res.status}: ${text}`
558
619
  };
559
620
  }
560
- return { branch: branch2, commitHash, remoteUrl: API_BASE, success: true };
621
+ return { branch: branch2, commitHash, remoteUrl: apiBase, success: true };
561
622
  } catch (err) {
562
623
  return {
563
624
  branch: branch2,
564
625
  commitHash,
565
- remoteUrl: API_BASE,
626
+ remoteUrl: apiBase,
566
627
  success: false,
567
628
  error: err instanceof Error ? err.message : String(err)
568
629
  };
@@ -603,19 +664,106 @@ async function fetchBranchCard(cardUrl, branch2, nitDir) {
603
664
  throw new Error(`Failed to fetch card: HTTP ${res.status}`);
604
665
  }
605
666
 
667
+ // src/config.ts
668
+ import { promises as fs5 } from "fs";
669
+ import { join as join5 } from "path";
670
+ var CONFIG_FILE = "config";
671
+ async function readConfig(nitDir) {
672
+ const configPath = join5(nitDir, CONFIG_FILE);
673
+ let raw;
674
+ try {
675
+ raw = await fs5.readFile(configPath, "utf-8");
676
+ } catch {
677
+ return { remotes: {} };
678
+ }
679
+ return parseConfig(raw);
680
+ }
681
+ async function writeConfig(nitDir, config) {
682
+ const configPath = join5(nitDir, CONFIG_FILE);
683
+ await fs5.writeFile(configPath, serializeConfig(config), "utf-8");
684
+ }
685
+ async function getRemoteUrl(nitDir, remoteName) {
686
+ const config = await readConfig(nitDir);
687
+ return config.remotes[remoteName]?.url ?? null;
688
+ }
689
+ async function getSkillsDir(nitDir) {
690
+ const config = await readConfig(nitDir);
691
+ return config.skillsDir ?? null;
692
+ }
693
+ function parseConfig(raw) {
694
+ const remotes = {};
695
+ let currentSection = null;
696
+ let currentRemote = null;
697
+ let skillsDir;
698
+ for (const line of raw.split("\n")) {
699
+ const trimmed = line.trim();
700
+ if (trimmed === "" || trimmed.startsWith("#")) continue;
701
+ const remoteMatch = trimmed.match(/^\[remote\s+"([^"]+)"\]$/);
702
+ if (remoteMatch) {
703
+ currentSection = "remote";
704
+ currentRemote = remoteMatch[1];
705
+ if (!remotes[currentRemote]) {
706
+ remotes[currentRemote] = {};
707
+ }
708
+ continue;
709
+ }
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) {
719
+ if (key === "url") {
720
+ remotes[currentRemote].url = value.trim();
721
+ } else if (key === "credential") {
722
+ remotes[currentRemote].credential = value.trim();
723
+ }
724
+ } else if (currentSection === "skills") {
725
+ if (key === "dir") {
726
+ skillsDir = value.trim();
727
+ }
728
+ }
729
+ }
730
+ }
731
+ return { remotes, skillsDir };
732
+ }
733
+ function serializeConfig(config) {
734
+ const lines = [];
735
+ for (const [name, remote2] of Object.entries(config.remotes)) {
736
+ lines.push(`[remote "${name}"]`);
737
+ if (remote2.url) {
738
+ lines.push(` url = ${remote2.url}`);
739
+ }
740
+ if (remote2.credential) {
741
+ lines.push(` credential = ${remote2.credential}`);
742
+ }
743
+ lines.push("");
744
+ }
745
+ if (config.skillsDir) {
746
+ lines.push("[skills]");
747
+ lines.push(` dir = ${config.skillsDir}`);
748
+ lines.push("");
749
+ }
750
+ return lines.join("\n");
751
+ }
752
+
606
753
  // src/index.ts
607
754
  var NIT_DIR = ".nit";
608
755
  var CARD_FILE = "agent-card.json";
756
+ var DEFAULT_API_BASE = "https://api.newtype-ai.org";
609
757
  function findNitDir(startDir) {
610
- let dir = resolve(startDir || process.cwd());
758
+ let dir = resolve2(startDir || process.cwd());
611
759
  while (true) {
612
- const candidate = join5(dir, NIT_DIR);
760
+ const candidate = join6(dir, NIT_DIR);
613
761
  try {
614
762
  const s = statSync(candidate);
615
763
  if (s.isDirectory()) return candidate;
616
764
  } catch {
617
765
  }
618
- const parent = resolve(dir, "..");
766
+ const parent = resolve2(dir, "..");
619
767
  if (parent === dir) {
620
768
  throw new Error(
621
769
  "Not a nit repository (or any parent directory). Run `nit init` first."
@@ -625,20 +773,20 @@ function findNitDir(startDir) {
625
773
  }
626
774
  }
627
775
  function projectDir(nitDir) {
628
- return resolve(nitDir, "..");
776
+ return resolve2(nitDir, "..");
629
777
  }
630
778
  async function readWorkingCard(nitDir) {
631
- const cardPath = join5(projectDir(nitDir), CARD_FILE);
779
+ const cardPath = join6(projectDir(nitDir), CARD_FILE);
632
780
  try {
633
- const raw = await fs5.readFile(cardPath, "utf-8");
781
+ const raw = await fs6.readFile(cardPath, "utf-8");
634
782
  return JSON.parse(raw);
635
783
  } catch {
636
784
  throw new Error(`Cannot read ${CARD_FILE}. Does it exist?`);
637
785
  }
638
786
  }
639
787
  async function writeWorkingCard(nitDir, card) {
640
- const cardPath = join5(projectDir(nitDir), CARD_FILE);
641
- await fs5.writeFile(cardPath, JSON.stringify(card, null, 2) + "\n", "utf-8");
788
+ const cardPath = join6(projectDir(nitDir), CARD_FILE);
789
+ await fs6.writeFile(cardPath, JSON.stringify(card, null, 2) + "\n", "utf-8");
642
790
  }
643
791
  async function getCardAtCommit(nitDir, commitHash) {
644
792
  const commitRaw = await readObject(nitDir, commitHash);
@@ -655,28 +803,28 @@ async function getAuthorName(nitDir) {
655
803
  }
656
804
  }
657
805
  async function init(options) {
658
- const projDir = resolve(options?.projectDir || process.cwd());
659
- const nitDir = join5(projDir, NIT_DIR);
806
+ const projDir = resolve2(options?.projectDir || process.cwd());
807
+ const nitDir = join6(projDir, NIT_DIR);
660
808
  try {
661
- await fs5.access(nitDir);
809
+ await fs6.access(nitDir);
662
810
  throw new Error("Already initialized. .nit/ directory exists.");
663
811
  } catch (err) {
664
812
  if (err instanceof Error && err.message.startsWith("Already")) throw err;
665
813
  }
666
- await fs5.mkdir(join5(nitDir, "objects"), { recursive: true });
667
- await fs5.mkdir(join5(nitDir, "refs", "heads"), { recursive: true });
668
- await fs5.mkdir(join5(nitDir, "refs", "remote"), { recursive: true });
669
- await fs5.mkdir(join5(nitDir, "identity"), { recursive: true });
670
- await fs5.mkdir(join5(nitDir, "logs"), { recursive: true });
814
+ await fs6.mkdir(join6(nitDir, "objects"), { recursive: true });
815
+ await fs6.mkdir(join6(nitDir, "refs", "heads"), { recursive: true });
816
+ await fs6.mkdir(join6(nitDir, "refs", "remote"), { recursive: true });
817
+ await fs6.mkdir(join6(nitDir, "identity"), { recursive: true });
818
+ await fs6.mkdir(join6(nitDir, "logs"), { recursive: true });
671
819
  const { publicKey: pubBase64 } = await generateKeypair(nitDir);
672
820
  const publicKeyField = formatPublicKeyField(pubBase64);
673
821
  const agentId = deriveAgentId(publicKeyField);
674
822
  await saveAgentId(nitDir, agentId);
675
- const cardPath = join5(projDir, CARD_FILE);
823
+ const cardPath = join6(projDir, CARD_FILE);
676
824
  let card;
677
825
  let skillsFound = [];
678
826
  try {
679
- const raw = await fs5.readFile(cardPath, "utf-8");
827
+ const raw = await fs6.readFile(cardPath, "utf-8");
680
828
  card = JSON.parse(raw);
681
829
  card.publicKey = publicKeyField;
682
830
  skillsFound = card.skills.map((s) => s.id);
@@ -715,13 +863,18 @@ async function init(options) {
715
863
  const commitHash = await writeObject(nitDir, "commit", commitContent);
716
864
  await setBranch(nitDir, "main", commitHash);
717
865
  await setHead(nitDir, "main");
718
- await fs5.writeFile(join5(nitDir, "logs", "HEAD"), "", "utf-8");
719
- await fs5.writeFile(join5(nitDir, "config"), "", "utf-8");
866
+ await fs6.writeFile(join6(nitDir, "logs", "HEAD"), "", "utf-8");
867
+ const skillsDir = await discoverSkillsDir(projDir);
868
+ await writeConfig(nitDir, {
869
+ remotes: { origin: { url: DEFAULT_API_BASE } },
870
+ skillsDir
871
+ });
720
872
  return {
721
873
  agentId,
722
874
  publicKey: publicKeyField,
723
875
  cardUrl: card.url,
724
- skillsFound
876
+ skillsFound,
877
+ skillsDir
725
878
  };
726
879
  }
727
880
  async function status(options) {
@@ -790,13 +943,38 @@ async function sign2(message, options) {
790
943
  }
791
944
  async function loginPayload(domain, options) {
792
945
  const nitDir = findNitDir(options?.projectDir);
946
+ let switchedBranch;
947
+ let createdSkill;
948
+ const currentBranch = await getCurrentBranch(nitDir);
949
+ if (currentBranch !== domain) {
950
+ const isNew = !await getBranch(nitDir, domain);
951
+ if (isNew) {
952
+ const headHash = await resolveHead(nitDir);
953
+ await setBranch(nitDir, domain, headHash);
954
+ }
955
+ await checkout(domain, options);
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
+ }
970
+ }
793
971
  const agentId = await loadAgentId(nitDir);
794
972
  const timestamp = Math.floor(Date.now() / 1e3);
795
973
  const message = `${agentId}
796
974
  ${domain}
797
975
  ${timestamp}`;
798
976
  const signature = await signMessage(nitDir, message);
799
- return { agent_id: agentId, domain, timestamp, signature };
977
+ return { agent_id: agentId, domain, timestamp, signature, switchedBranch, createdSkill };
800
978
  }
801
979
  async function commit(message, options) {
802
980
  const nitDir = findNitDir(options?.projectDir);
@@ -912,6 +1090,7 @@ async function checkout(branchName, options) {
912
1090
  async function push(options) {
913
1091
  const nitDir = findNitDir(options?.projectDir);
914
1092
  const remoteName = options?.remoteName || "origin";
1093
+ const apiBase = await getRemoteUrl(nitDir, remoteName) || DEFAULT_API_BASE;
915
1094
  const branches = await listBranches(nitDir);
916
1095
  const currentBranch = await getCurrentBranch(nitDir);
917
1096
  const toPush = options?.all ? branches : branches.filter((b) => b.name === currentBranch);
@@ -925,7 +1104,7 @@ async function push(options) {
925
1104
  const cardJson = await readObject(nitDir, c.card);
926
1105
  const result = await pushBranch(
927
1106
  nitDir,
928
- remoteName,
1107
+ apiBase,
929
1108
  b.name,
930
1109
  cardJson,
931
1110
  b.commitHash
@@ -939,14 +1118,36 @@ async function push(options) {
939
1118
  }
940
1119
  async function remote(options) {
941
1120
  const nitDir = findNitDir(options?.projectDir);
942
- const card = await readWorkingCard(nitDir);
1121
+ const remoteUrl = await getRemoteUrl(nitDir, "origin");
943
1122
  const agentId = await loadAgentId(nitDir);
944
1123
  return {
945
1124
  name: "origin",
946
- url: card.url || "(not set)",
1125
+ url: remoteUrl || DEFAULT_API_BASE,
947
1126
  agentId
948
1127
  };
949
1128
  }
1129
+ async function remoteAdd(name, url, options) {
1130
+ const nitDir = findNitDir(options?.projectDir);
1131
+ const config = await readConfig(nitDir);
1132
+ if (config.remotes[name]) {
1133
+ throw new Error(
1134
+ `Remote "${name}" already exists. Use 'nit remote set-url ${name} <url>' to change it.`
1135
+ );
1136
+ }
1137
+ config.remotes[name] = { url };
1138
+ await writeConfig(nitDir, config);
1139
+ }
1140
+ async function remoteSetUrl(name, url, options) {
1141
+ const nitDir = findNitDir(options?.projectDir);
1142
+ const config = await readConfig(nitDir);
1143
+ if (!config.remotes[name]) {
1144
+ throw new Error(
1145
+ `Remote "${name}" does not exist. Use 'nit remote add ${name} <url>' to create it.`
1146
+ );
1147
+ }
1148
+ config.remotes[name].url = url;
1149
+ await writeConfig(nitDir, config);
1150
+ }
950
1151
 
951
1152
  export {
952
1153
  formatPublicKeyField,
@@ -971,5 +1172,7 @@ export {
971
1172
  branch,
972
1173
  checkout,
973
1174
  push,
974
- remote
1175
+ remote,
1176
+ remoteAdd,
1177
+ remoteSetUrl
975
1178
  };
package/dist/cli.js CHANGED
@@ -11,9 +11,11 @@ import {
11
11
  loginPayload,
12
12
  push,
13
13
  remote,
14
+ remoteAdd,
15
+ remoteSetUrl,
14
16
  sign,
15
17
  status
16
- } from "./chunk-AEWDQDBM.js";
18
+ } from "./chunk-U6PEH4IJ.js";
17
19
 
18
20
  // src/cli.ts
19
21
  var bold = (s) => `\x1B[1m${s}\x1B[0m`;
@@ -78,6 +80,7 @@ async function cmdInit() {
78
80
  console.log(` Agent ID: ${green(result.agentId)}`);
79
81
  console.log(` Public key: ${dim(result.publicKey)}`);
80
82
  console.log(` Card URL: ${result.cardUrl}`);
83
+ console.log(` Skills dir: ${dim(result.skillsDir)}`);
81
84
  if (result.skillsFound.length > 0) {
82
85
  console.log(` Skills: ${result.skillsFound.join(", ")}`);
83
86
  } else {
@@ -185,6 +188,34 @@ async function cmdPush(args) {
185
188
  }
186
189
  }
187
190
  async function cmdRemote(args) {
191
+ const subcommand = args[0];
192
+ if (subcommand === "set-url") {
193
+ const name = args[1];
194
+ const url = args[2];
195
+ if (!name || !url) {
196
+ console.error("Usage: nit remote set-url <name> <url>");
197
+ process.exit(1);
198
+ }
199
+ await remoteSetUrl(name, url);
200
+ console.log(`Set URL for '${name}' to ${url}`);
201
+ return;
202
+ }
203
+ if (subcommand === "add") {
204
+ const name = args[1];
205
+ const url = args[2];
206
+ if (!name || !url) {
207
+ console.error("Usage: nit remote add <name> <url>");
208
+ process.exit(1);
209
+ }
210
+ await remoteAdd(name, url);
211
+ console.log(`Added remote '${green(name)}' \u2192 ${url}`);
212
+ return;
213
+ }
214
+ if (subcommand) {
215
+ console.error(`nit remote: unknown subcommand '${subcommand}'`);
216
+ console.error("Usage: nit remote [set-url <name> <url> | add <name> <url>]");
217
+ process.exit(1);
218
+ }
188
219
  const info = await remote();
189
220
  console.log(`${bold(info.name)}`);
190
221
  console.log(` URL: ${info.url}`);
@@ -200,7 +231,14 @@ async function cmdSign(args) {
200
231
  process.exit(1);
201
232
  }
202
233
  const payload = await loginPayload(domain);
203
- console.log(JSON.stringify(payload, null, 2));
234
+ if (payload.switchedBranch) {
235
+ console.error(`Switched to branch '${payload.switchedBranch}'`);
236
+ }
237
+ if (payload.createdSkill) {
238
+ console.error(`Created skill template '${payload.createdSkill}'`);
239
+ }
240
+ const { switchedBranch: _s, createdSkill: _c, ...output } = payload;
241
+ console.log(JSON.stringify(output, null, 2));
204
242
  return;
205
243
  }
206
244
  const message = args[0];
@@ -228,8 +266,10 @@ ${bold("Commands:")}
228
266
  checkout <branch> Switch branch (overwrites agent-card.json)
229
267
  push [--all] Push branch(es) to remote
230
268
  sign "message" Sign a message with your Ed25519 key
231
- sign --login <dom> Generate login payload for an app
269
+ sign --login <dom> Switch to domain branch + generate login payload
232
270
  remote Show remote info
271
+ remote add <n> <u> Add a new remote
272
+ remote set-url <n> <u> Change remote URL
233
273
 
234
274
  ${bold("Examples:")}
235
275
  nit init
package/dist/index.d.ts CHANGED
@@ -27,6 +27,8 @@ interface NitHead {
27
27
  }
28
28
  /** Remote configuration for a single named remote. */
29
29
  interface NitRemoteConfig {
30
+ /** API base URL (e.g. "https://api.newtype-ai.org") */
31
+ url?: string;
30
32
  /** Legacy field — push auth is now via Ed25519 keypair */
31
33
  credential?: string;
32
34
  }
@@ -34,6 +36,8 @@ interface NitRemoteConfig {
34
36
  interface NitConfig {
35
37
  /** Keyed by remote name (e.g. "origin") */
36
38
  remotes: Record<string, NitRemoteConfig>;
39
+ /** Discovered skills directory path */
40
+ skillsDir?: string;
37
41
  }
38
42
  /** A2A-compatible agent card. */
39
43
  interface AgentCard {
@@ -54,11 +58,13 @@ interface AgentCard {
54
58
  url?: string;
55
59
  };
56
60
  }
57
- /** 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. */
58
64
  interface AgentCardSkill {
59
65
  id: string;
60
- name: string;
61
- description: string;
66
+ name?: string;
67
+ description?: string;
62
68
  tags?: string[];
63
69
  examples?: string[];
64
70
  inputModes?: string[];
@@ -198,6 +204,7 @@ interface InitResult {
198
204
  publicKey: string;
199
205
  cardUrl: string;
200
206
  skillsFound: string[];
207
+ skillsDir: string;
201
208
  }
202
209
  /**
203
210
  * Initialize a new nit repository in the project directory.
@@ -225,12 +232,16 @@ declare function sign(message: string, options?: {
225
232
  }): Promise<string>;
226
233
  /**
227
234
  * Generate a login payload for app authentication.
228
- * Constructs the canonical message ({agent_id}\n{domain}\n{timestamp}),
235
+ * Automatically switches to (or creates) the domain branch,
236
+ * then constructs the canonical message ({agent_id}\n{domain}\n{timestamp}),
229
237
  * signs it, and returns the full payload ready to send to an app.
230
238
  */
231
239
  declare function loginPayload(domain: string, options?: {
232
240
  projectDir?: string;
233
- }): Promise<LoginPayload>;
241
+ }): Promise<LoginPayload & {
242
+ switchedBranch?: string;
243
+ createdSkill?: string;
244
+ }>;
234
245
  /**
235
246
  * Snapshot the current agent-card.json as a new commit.
236
247
  * Resolves skill pointers from SKILL.md before committing.
@@ -282,10 +293,22 @@ interface RemoteInfo {
282
293
  agentId: string;
283
294
  }
284
295
  /**
285
- * Show remote info. URL comes from agent-card.json, agent ID from .nit/identity/.
296
+ * Show remote info. URL comes from .nit/config, agent ID from .nit/identity/.
286
297
  */
287
298
  declare function remote(options?: {
288
299
  projectDir?: string;
289
300
  }): Promise<RemoteInfo>;
301
+ /**
302
+ * Add a new named remote with a URL.
303
+ */
304
+ declare function remoteAdd(name: string, url: string, options?: {
305
+ projectDir?: string;
306
+ }): Promise<void>;
307
+ /**
308
+ * Change the URL for an existing remote.
309
+ */
310
+ declare function remoteSetUrl(name: string, url: string, options?: {
311
+ projectDir?: string;
312
+ }): Promise<void>;
290
313
 
291
- export { type AgentCard, type AgentCardSkill, type DiffResult, type FieldDiff, type InitResult, type LoginPayload, NIT_NAMESPACE, type NitBranch, type NitCommit, type NitConfig, type NitHead, type NitRemoteConfig, type PushResult, type RemoteInfo, type SkillMetadata, type StatusResult, branch, checkout, commit, deriveAgentId, diff, diffCards, fetchBranchCard, findNitDir, formatDiff, formatPublicKeyField, init, loadAgentId, log, loginPayload, parsePublicKeyField, push, remote, sign, signChallenge, signMessage, status, verifySignature };
314
+ export { type AgentCard, type AgentCardSkill, type DiffResult, type FieldDiff, type InitResult, type LoginPayload, NIT_NAMESPACE, type NitBranch, type NitCommit, type NitConfig, type NitHead, type NitRemoteConfig, type PushResult, type RemoteInfo, type SkillMetadata, type StatusResult, branch, checkout, commit, deriveAgentId, diff, diffCards, fetchBranchCard, findNitDir, formatDiff, formatPublicKeyField, init, loadAgentId, log, loginPayload, parsePublicKeyField, push, remote, remoteAdd, remoteSetUrl, sign, signChallenge, signMessage, status, verifySignature };
package/dist/index.js CHANGED
@@ -18,12 +18,14 @@ import {
18
18
  parsePublicKeyField,
19
19
  push,
20
20
  remote,
21
+ remoteAdd,
22
+ remoteSetUrl,
21
23
  sign,
22
24
  signChallenge,
23
25
  signMessage,
24
26
  status,
25
27
  verifySignature
26
- } from "./chunk-AEWDQDBM.js";
28
+ } from "./chunk-U6PEH4IJ.js";
27
29
  export {
28
30
  NIT_NAMESPACE,
29
31
  branch,
@@ -43,6 +45,8 @@ export {
43
45
  parsePublicKeyField,
44
46
  push,
45
47
  remote,
48
+ remoteAdd,
49
+ remoteSetUrl,
46
50
  sign,
47
51
  signChallenge,
48
52
  signMessage,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@newtype-ai/nit",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "Version control for agent cards",
5
5
  "type": "module",
6
6
  "bin": {