@codeursenior/pek 0.1.9 → 0.1.10

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/dist/cli.js CHANGED
@@ -11,7 +11,7 @@ Usage: npx @codeursenior/pek audit agent [folder] [options]
11
11
 
12
12
  Explore context, skills and MCP configuration in your default browser.
13
13
 
14
- --port <number> Choose a port (default: an available port)
14
+ --port <number> Choose a port (default: 4242; 0 for an available port)
15
15
  --no-open Print the URL without opening a browser
16
16
  --project-only Skip user-level assets
17
17
  --help, -h Show this help
@@ -42,7 +42,7 @@ try {
42
42
  positionals[1] !== "agent" ||
43
43
  positionals.length > 3)
44
44
  throw new Error("Expected: pek audit agent [folder]. Use --help for options.");
45
- const port = values.port === undefined ? 0 : Number(values.port);
45
+ const port = values.port === undefined ? 4242 : Number(values.port);
46
46
  if (!Number.isInteger(port) || port < 0 || port > 65535)
47
47
  throw new Error("Port must be an integer between 0 and 65535.");
48
48
  const root = path.resolve(positionals[2] || process.cwd());
@@ -82,6 +82,6 @@ try {
82
82
  }
83
83
  }
84
84
  catch (error) {
85
- console.error(`\n ${errorCode(error) === "EADDRINUSE" ? "Port is in use. Choose another --port or omit it." : error instanceof Error ? error.message : String(error)}\n`);
85
+ console.error(`\n ${errorCode(error) === "EADDRINUSE" ? "Port is in use (default: 4242). Close the other instance or choose --port <number>. Whiteboards are stored separately for each browser and port." : error instanceof Error ? error.message : String(error)}\n`);
86
86
  process.exitCode = 1;
87
87
  }
@@ -0,0 +1,39 @@
1
+ import path from "node:path";
2
+ import { links } from "./metadata.js";
3
+ import { referenceResolver } from "./references.js";
4
+ /** Explicit references only. A mention in prose is not a dependency. */
5
+ export function skillGraph(entries, skills, env) {
6
+ const files = entries.filter((entry) => path.basename(entry.file) === "SKILL.md");
7
+ const byFile = new Map(skills.flatMap((skill) => skill.editTargets.map((target) => [target.id, skill])));
8
+ const resolve = referenceResolver(files, env);
9
+ const edges = new Map();
10
+ for (const entry of files) {
11
+ const source = byFile.get(entry.id);
12
+ if (!source)
13
+ continue;
14
+ const add = (target) => {
15
+ if (target && target.id !== source.id)
16
+ edges.set(`${source.id}:${target.id}`, {
17
+ source: source.id,
18
+ target: target.id,
19
+ });
20
+ };
21
+ const references = links(entry.text);
22
+ // Also accept unquoted paths, including absolute paths; resolve only indexed skills.
23
+ for (const match of entry.text.matchAll(/(?:^|[\s`(<])((?:~\/|\.{0,2}\/)?[\w.\-/À-ž]+\/SKILL\.md)(?=$|[\s`)>.,;#])/gm))
24
+ references.push({ target: match[1] ?? "", wiki: false });
25
+ for (const reference of references) {
26
+ const target = resolve(entry, reference);
27
+ if (target)
28
+ add(byFile.get(target.id));
29
+ }
30
+ const body = entry.text.replace(/^---\r?\n[\s\S]*?\r?\n---(?:\r?\n|$)/, "");
31
+ for (const match of body.matchAll(/(?:^|[\s`(])(?:\/|\$)([a-zA-Z0-9][\w:-]*)(?=$|[\s`.,;!?)\]])/gm)) {
32
+ const candidates = skills.filter((skill) => skill.name === match[1]);
33
+ // Names shared by different skills are ambiguous; paths remain resolvable.
34
+ if (candidates.length === 1)
35
+ add(candidates[0]);
36
+ }
37
+ }
38
+ return [...edges.values()];
39
+ }
package/dist/scanner.js CHANGED
@@ -6,6 +6,7 @@ import { collectFiles } from "./scan/collect.js";
6
6
  import { buildGraph } from "./scan/graph.js";
7
7
  import { discoverIdentity } from "./scan/identity.js";
8
8
  import { discoverMcp } from "./scan/mcp.js";
9
+ import { skillGraph } from "./scan/skill-graph.js";
9
10
  import { inside, slash } from "./scan/values.js";
10
11
  import { contextInventory } from "./token-estimator.js";
11
12
  /** Inventory only: never executes instructions, hooks, skills or MCP commands. */
@@ -55,6 +56,7 @@ export async function scanProject(project, options = {}) {
55
56
  rules: [...rules.values()].sort((a, b) => a.name.localeCompare(b.name)),
56
57
  edges,
57
58
  skills: [...skills.values()].sort((a, b) => a.name.localeCompare(b.name)),
59
+ skillEdges: skillGraph(entries, [...skills.values()], env),
58
60
  mcp,
59
61
  warnings: [...new Set(env.warnings)],
60
62
  limits: { includeUser, scannedFiles: inspected, truncated: capped },
@@ -14,6 +14,8 @@ The Angular UI uses standalone OnPush components and signals. `WorkspaceState` o
14
14
 
15
15
  The detail component owns its edit draft and revision. A request sequence prevents late previews or writes from updating a different selection. The graph component delegates only its SVG subtree to the typed D3 renderer. Simulation nodes and links are copies of API data. Initial positions, layout forces and bounded warm-up are preserved; cleanup stops simulations and removes listeners when the graph is replaced or destroyed.
16
16
 
17
+ The Skills whiteboard is a separate Angular SVG view. `scan/skill-graph.ts` resolves explicit links, paths and invocations against the indexed skill inventory, without reading arbitrary targets or inferring relations from prose. The API returns these read-only skill edges separately from context edges. Ambiguous invocations are left unconnected. `skill-board-model.ts` reconciles browser-saved placements against stable source-file identities, keeping content edits independent of placement and appending added skills beside the composition. Manual arrows and titles live only in project-keyed localStorage. Read-only source edges are rebuilt from each scan and never saved as drawings. Invalid or blocked browser storage produces a visible status; unreadable saved data is not overwritten. The CLI uses port 4242 to keep the browser origin stable; the programmatic server continues to accept ephemeral ports.
18
+
17
19
  ## Extending and testing
18
20
 
19
21
  - Add filesystem discovery behavior to the relevant scan module, with a synthetic fixture in the backend tests. Test normalization and reference rules as pure functions when possible; retain real filesystem tests for symlinks, scope boundaries, and limits.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codeursenior/pek",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "Agent Visualizer by Product Engineer Kit: explore local AI instructions, rules, skills and MCP configuration.",
5
5
  "type": "module",
6
6
  "bin": {