agent-first-data 0.13.1 → 0.14.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/README.md CHANGED
@@ -20,7 +20,7 @@ console.log(outputJson(value));
20
20
  console.log(outputPlain(value));
21
21
  ```
22
22
 
23
- Useful names use TypeScript casing: `outputJson`, `outputYaml`, `outputPlain`, `outputJsonWithOptions`, `redactedValue`, `redactSecretsInPlace`, `redactUrlSecrets`, `parseSize`, `normalizeUtcOffset`, `cliParseOutput`, `cliOutput`, `buildCliError`, `buildCliVersion`, and `cliHandleVersionOrContinue`.
23
+ Useful names use TypeScript casing: `outputJson`, `outputYaml`, `outputPlain`, `outputJsonWithOptions`, `redactedValue`, `redactSecretsInPlace`, `redactUrlSecrets`, `parseSize`, `normalizeUtcOffset`, `isValidRfc3339Date`, `isValidRfc3339Time`, `cliParseOutput`, `cliOutput`, `buildCliError`, `buildCliVersion`, and `cliHandleVersionOrContinue`.
24
24
 
25
25
  Logging is available through `initJson`, `initPlain`, `initYaml`, `span`, and `log`.
26
26
 
@@ -41,7 +41,7 @@ initJson({ secretNames: ["authorization"] });
41
41
 
42
42
  ## Reference
43
43
 
44
- - Full convention and API groups: [../docs/overview.md](../docs/overview.md)
45
- - Formal cross-language contract: [../spec/agent-first-data.md](../spec/agent-first-data.md)
46
- - Conformance fixtures: [../spec/fixtures](../spec/fixtures)
47
- - Agent skill: [../skills/agent-first-data.md](../skills/agent-first-data.md)
44
+ - Full convention and API groups: [docs/overview.md](https://github.com/agentfirstkit/agent-first-data/blob/main/docs/overview.md)
45
+ - Formal cross-language contract: [spec/agent-first-data.md](https://github.com/agentfirstkit/agent-first-data/blob/main/spec/agent-first-data.md)
46
+ - Conformance fixtures: [spec/fixtures](https://github.com/agentfirstkit/agent-first-data/tree/main/spec/fixtures)
47
+ - Agent skill: [skills/agent-first-data.md](https://github.com/agentfirstkit/agent-first-data/blob/main/skills/agent-first-data.md)
package/dist/format.d.ts CHANGED
@@ -1,10 +1,11 @@
1
1
  /**
2
2
  * AFDATA output formatting and protocol templates.
3
3
  *
4
- * 19 public APIs and 4 types: protocol builders, value redactors (copy and
4
+ * 21 public APIs and 4 types: protocol builders, value redactors (copy and
5
5
  * in-place; cover _secret and _url fields), output formatters, URL-string
6
6
  * redactors (redactUrlSecrets / WithOptions), parseSize, normalizeUtcOffset,
7
- * RedactionPolicy, RedactionOptions, OutputStyle, and OutputOptions.
7
+ * isValidRfc3339Date, isValidRfc3339Time, RedactionPolicy, RedactionOptions,
8
+ * OutputStyle, and OutputOptions.
8
9
  */
9
10
  export type JsonValue = string | number | boolean | null | JsonValue[] | {
10
11
  [key: string]: JsonValue;
@@ -94,3 +95,7 @@ export declare function parseSize(s: string): number | null;
94
95
  * are intentionally out of scope.
95
96
  */
96
97
  export declare function normalizeUtcOffset(value: string): string | null;
98
+ /** Return true when value is an RFC 3339 full-date (YYYY-MM-DD). */
99
+ export declare function isValidRfc3339Date(value: string): boolean;
100
+ /** Return true when value is an RFC 3339 partial-time (HH:MM:SS[.fraction]). */
101
+ export declare function isValidRfc3339Time(value: string): boolean;
package/dist/format.js CHANGED
@@ -1,10 +1,11 @@
1
1
  /**
2
2
  * AFDATA output formatting and protocol templates.
3
3
  *
4
- * 19 public APIs and 4 types: protocol builders, value redactors (copy and
4
+ * 21 public APIs and 4 types: protocol builders, value redactors (copy and
5
5
  * in-place; cover _secret and _url fields), output formatters, URL-string
6
6
  * redactors (redactUrlSecrets / WithOptions), parseSize, normalizeUtcOffset,
7
- * RedactionPolicy, RedactionOptions, OutputStyle, and OutputOptions.
7
+ * isValidRfc3339Date, isValidRfc3339Time, RedactionPolicy, RedactionOptions,
8
+ * OutputStyle, and OutputOptions.
8
9
  */
9
10
  // ═══════════════════════════════════════════
10
11
  // Public API: Protocol Builders
@@ -202,6 +203,32 @@ export function normalizeUtcOffset(value) {
202
203
  return "UTC";
203
204
  return `${s[0]}${hours.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")}`;
204
205
  }
206
+ /** Return true when value is an RFC 3339 full-date (YYYY-MM-DD). */
207
+ export function isValidRfc3339Date(value) {
208
+ if (value.length !== 10 || value[4] !== "-" || value[7] !== "-")
209
+ return false;
210
+ const year = parseFixedDigits(value.slice(0, 4));
211
+ const month = parseFixedDigits(value.slice(5, 7));
212
+ const day = parseFixedDigits(value.slice(8, 10));
213
+ if (year === null || month === null || day === null)
214
+ return false;
215
+ return month >= 1 && month <= 12 && day >= 1 && day <= daysInMonth(year, month);
216
+ }
217
+ /** Return true when value is an RFC 3339 partial-time (HH:MM:SS[.fraction]). */
218
+ export function isValidRfc3339Time(value) {
219
+ if (value.length < 8 || value[2] !== ":" || value[5] !== ":")
220
+ return false;
221
+ const hour = parseFixedDigits(value.slice(0, 2));
222
+ const minute = parseFixedDigits(value.slice(3, 5));
223
+ const second = parseFixedDigits(value.slice(6, 8));
224
+ if (hour === null || minute === null || second === null)
225
+ return false;
226
+ if (hour > 23 || minute > 59 || second > 59)
227
+ return false;
228
+ if (value.length === 8)
229
+ return true;
230
+ return value[8] === "." && value.length > 9 && /^[0-9]+$/.test(value.slice(9));
231
+ }
205
232
  function parseUtcOffsetBody(body) {
206
233
  if (!body)
207
234
  return null;
@@ -224,6 +251,23 @@ function parseUtcOffsetBody(body) {
224
251
  return [Number(body.slice(0, 2)), Number(body.slice(2))];
225
252
  return null;
226
253
  }
254
+ function parseFixedDigits(value) {
255
+ if (!/^[0-9]+$/.test(value))
256
+ return null;
257
+ return Number(value);
258
+ }
259
+ function daysInMonth(year, month) {
260
+ if ([1, 3, 5, 7, 8, 10, 12].includes(month))
261
+ return 31;
262
+ if ([4, 6, 9, 11].includes(month))
263
+ return 30;
264
+ if (month === 2)
265
+ return isLeapYear(year) ? 29 : 28;
266
+ return 0;
267
+ }
268
+ function isLeapYear(year) {
269
+ return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
270
+ }
227
271
  const DEFAULT_CONTEXT = {
228
272
  secretNames: new Set(),
229
273
  };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Agent-First Data (AFDATA) — suffix-driven output formatting and protocol templates.
3
3
  */
4
- export { buildJsonOk, buildJsonError, buildJson, type JsonValue, RedactionPolicy, type RedactionOptions, OutputStyle, type OutputOptions, outputJson, outputJsonWith, outputJsonWithOptions, outputYaml, outputYamlWithOptions, outputPlain, outputPlainWithOptions, redactSecretsInPlace, redactSecretsInPlaceWithOptions, redactedValue, redactedValueWith, redactedValueWithOptions, redactUrlSecrets, redactUrlSecretsWithOptions, parseSize, normalizeUtcOffset, } from "./format.js";
4
+ export { buildJsonOk, buildJsonError, buildJson, type JsonValue, RedactionPolicy, type RedactionOptions, OutputStyle, type OutputOptions, outputJson, outputJsonWith, outputJsonWithOptions, outputYaml, outputYamlWithOptions, outputPlain, outputPlainWithOptions, redactSecretsInPlace, redactSecretsInPlaceWithOptions, redactedValue, redactedValueWith, redactedValueWithOptions, redactUrlSecrets, redactUrlSecretsWithOptions, parseSize, normalizeUtcOffset, isValidRfc3339Date, isValidRfc3339Time, } from "./format.js";
5
5
  export { log, span, initJson, initPlain, initYaml } from "./afdata_logging.js";
6
6
  export { type OutputFormat, cliParseOutput, cliParseLogFilters, cliOutput, cliOutputWithOptions, buildCliError, buildCliVersion, cliRenderVersion, cliHandleVersionOrContinue, } from "./cli.js";
7
7
  export { type SkillSpec, type SkillAction, type SkillScope, type SkillAgentSelection, type SkillAgent, type SkillOptions, type SkillTargetStatus, type SkillUninstallStatus, type SkillReport, SkillError, runSkillAdmin, } from "./skill.js";
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Agent-First Data (AFDATA) — suffix-driven output formatting and protocol templates.
3
3
  */
4
- export { buildJsonOk, buildJsonError, buildJson, RedactionPolicy, OutputStyle, outputJson, outputJsonWith, outputJsonWithOptions, outputYaml, outputYamlWithOptions, outputPlain, outputPlainWithOptions, redactSecretsInPlace, redactSecretsInPlaceWithOptions, redactedValue, redactedValueWith, redactedValueWithOptions, redactUrlSecrets, redactUrlSecretsWithOptions, parseSize, normalizeUtcOffset, } from "./format.js";
4
+ export { buildJsonOk, buildJsonError, buildJson, RedactionPolicy, OutputStyle, outputJson, outputJsonWith, outputJsonWithOptions, outputYaml, outputYamlWithOptions, outputPlain, outputPlainWithOptions, redactSecretsInPlace, redactSecretsInPlaceWithOptions, redactedValue, redactedValueWith, redactedValueWithOptions, redactUrlSecrets, redactUrlSecretsWithOptions, parseSize, normalizeUtcOffset, isValidRfc3339Date, isValidRfc3339Time, } from "./format.js";
5
5
  export { log, span, initJson, initPlain, initYaml } from "./afdata_logging.js";
6
6
  export { cliParseOutput, cliParseLogFilters, cliOutput, cliOutputWithOptions, buildCliError, buildCliVersion, cliRenderVersion, cliHandleVersionOrContinue, } from "./cli.js";
7
7
  export { SkillError, runSkillAdmin, } from "./skill.js";
package/dist/skill.d.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * A spore that embeds its `SKILL.md` describes itself with a {@link SkillSpec} and calls
5
5
  * {@link runSkillAdmin} to install, uninstall, or report status of that skill across
6
- * supported coding agents (Codex, Claude Code, opencode).
6
+ * supported coding agents (Codex, Claude Code, opencode, Hermes).
7
7
  *
8
8
  * The function performs the filesystem work and returns the protocol value (rendered by
9
9
  * the caller with `cliOutput`) or throws a {@link SkillError}. It never writes to
@@ -26,11 +26,11 @@ export interface SkillSpec {
26
26
  /** The skill action to perform. */
27
27
  export type SkillAction = "status" | "install" | "uninstall";
28
28
  /** Where to install the skill. */
29
- export type SkillScope = "personal" | "project";
29
+ export type SkillScope = "personal" | "workspace";
30
30
  /** Which agent target(s) to manage. */
31
- export type SkillAgentSelection = "all" | "codex" | "claude-code" | "opencode";
31
+ export type SkillAgentSelection = "all" | "codex" | "claude-code" | "opencode" | "hermes";
32
32
  /** A concrete agent a skill is installed for (no `all`). */
33
- export type SkillAgent = "codex" | "claude-code" | "opencode";
33
+ export type SkillAgent = "codex" | "claude-code" | "opencode" | "hermes";
34
34
  /** Options shared by every skill action. */
35
35
  export interface SkillOptions {
36
36
  agent: SkillAgentSelection;
package/dist/skill.js CHANGED
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * A spore that embeds its `SKILL.md` describes itself with a {@link SkillSpec} and calls
5
5
  * {@link runSkillAdmin} to install, uninstall, or report status of that skill across
6
- * supported coding agents (Codex, Claude Code, opencode).
6
+ * supported coding agents (Codex, Claude Code, opencode, Hermes).
7
7
  *
8
8
  * The function performs the filesystem work and returns the protocol value (rendered by
9
9
  * the caller with `cliOutput`) or throws a {@link SkillError}. It never writes to
@@ -128,20 +128,19 @@ function resolveTargets(spec, options) {
128
128
  resolveTarget(spec, "codex", "personal", undefined),
129
129
  resolveTarget(spec, "claude-code", "personal", undefined),
130
130
  resolveTarget(spec, "opencode", "personal", undefined),
131
+ resolveTarget(spec, "hermes", "personal", undefined),
131
132
  ];
132
133
  }
133
- // Codex has no project scope, so --agent all --scope project skips it.
134
- if (agent === "all" && scope === "project") {
134
+ if (agent === "all" && scope === "workspace") {
135
135
  return [
136
- resolveTarget(spec, "claude-code", "project", undefined),
137
- resolveTarget(spec, "opencode", "project", undefined),
136
+ resolveTarget(spec, "codex", "workspace", undefined),
137
+ resolveTarget(spec, "claude-code", "workspace", undefined),
138
+ resolveTarget(spec, "opencode", "workspace", undefined),
139
+ resolveTarget(spec, "hermes", "workspace", undefined),
138
140
  ];
139
141
  }
140
- if (agent === "codex" && scope === "project") {
141
- throw new SkillError("Codex project skill scope is not supported", "use personal scope for Codex, or --agent claude-code/opencode --scope project");
142
- }
143
142
  if (agent === "codex") {
144
- return [resolveTarget(spec, "codex", "personal", options.skillsDir)];
143
+ return [resolveTarget(spec, "codex", scope, options.skillsDir)];
145
144
  }
146
145
  if (agent === "claude-code") {
147
146
  return [resolveTarget(spec, "claude-code", scope, options.skillsDir)];
@@ -149,6 +148,9 @@ function resolveTargets(spec, options) {
149
148
  if (agent === "opencode") {
150
149
  return [resolveTarget(spec, "opencode", scope, options.skillsDir)];
151
150
  }
151
+ if (agent === "hermes") {
152
+ return [resolveTarget(spec, "hermes", scope, options.skillsDir)];
153
+ }
152
154
  throw new SkillError(`invalid --agent '${agent}'`);
153
155
  }
154
156
  function resolveTarget(spec, agent, scope, skillsDir) {
@@ -164,8 +166,8 @@ function resolveTarget(spec, agent, scope, skillsDir) {
164
166
  }
165
167
  function defaultSkillsDir(agent, scope) {
166
168
  if (agent === "codex") {
167
- if (scope === "project") {
168
- throw new SkillError("Codex project skill scope is not supported");
169
+ if (scope !== "personal") {
170
+ return join(process.cwd(), ".codex", "skills");
169
171
  }
170
172
  const codexHome = process.env["CODEX_HOME"];
171
173
  if (codexHome !== undefined) {
@@ -174,20 +176,30 @@ function defaultSkillsDir(agent, scope) {
174
176
  return join(homeDir(), ".codex", "skills");
175
177
  }
176
178
  if (agent === "claude-code") {
177
- if (scope === "project") {
179
+ if (scope !== "personal") {
178
180
  return join(process.cwd(), ".claude", "skills");
179
181
  }
180
182
  return join(homeDir(), ".claude", "skills");
181
183
  }
182
- // opencode
183
- if (scope === "project") {
184
- return join(process.cwd(), ".opencode", "skills");
184
+ if (agent === "opencode") {
185
+ if (scope !== "personal") {
186
+ return join(process.cwd(), ".opencode", "skills");
187
+ }
188
+ const xdg = process.env["XDG_CONFIG_HOME"];
189
+ if (xdg !== undefined) {
190
+ return join(xdg, "opencode", "skills");
191
+ }
192
+ return join(homeDir(), ".config", "opencode", "skills");
193
+ }
194
+ // hermes
195
+ if (scope !== "personal") {
196
+ return join(process.cwd(), ".hermes", "skills");
185
197
  }
186
- const xdg = process.env["XDG_CONFIG_HOME"];
187
- if (xdg !== undefined) {
188
- return join(xdg, "opencode", "skills");
198
+ const hermesHome = process.env["HERMES_HOME"];
199
+ if (hermesHome !== undefined) {
200
+ return join(hermesHome, "skills");
189
201
  }
190
- return join(homeDir(), ".config", "opencode", "skills");
202
+ return join(homeDir(), ".hermes", "skills");
191
203
  }
192
204
  function targetStatus(spec, target) {
193
205
  const st = lstatOrNull(target.skillPath);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-first-data",
3
- "version": "0.13.1",
3
+ "version": "0.14.0",
4
4
  "description": "A naming convention that lets AI agents understand your data without being told what it means.",
5
5
  "license": "MIT",
6
6
  "repository": {