@ggdna/dna-gg 1.3.0 → 1.4.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
@@ -52,10 +52,12 @@ All skills of this layer are prefixed with `gg-`:
52
52
  prompts wait in `~/.claude/pm-prompts/sessions/` and are committed to the
53
53
  branch `prompts` of that repo when a session starts or ends, under
54
54
  `doc/prompts/<first name>/<yyyy-mm>/<yyyy-mm-dd>_<repo>_<session>.md`.
55
- The first name comes from the `Git` line of `doc/team/people.md` there,
56
- an unknown email goes to `doc/prompts/_unknown/`. Only the prompts are
57
- kept, never the answers; tokens and passwords are replaced by
58
- `[redacted]`. No working tree is touched: the commit is built in a bare
55
+ The first name comes from `doc/team/people.md` there, found by the git
56
+ user name: a first name equal to it or to its first word, or a `Git`
57
+ line listing it. An unknown name goes to `doc/prompts/_unknown/`. No
58
+ e-mail address is read or written; the commits carry the neutral author
59
+ `pm-prompts`. Only the prompts are kept, never the answers; tokens and
60
+ passwords are replaced by `[redacted]`. No working tree is touched: the commit is built in a bare
59
61
  cache below `~/.claude/pm-prompts/repos/`. Needs `node` and `git`; a
60
62
  failure never stops a prompt and ends up in
61
63
  `~/.claude/pm-prompts/sync.log`.
@@ -39,7 +39,7 @@
39
39
  "ecosystem": null,
40
40
  "resolvedVersion": null,
41
41
  "via": null,
42
- "hash": "0x7e5e4bbb754c4f13"
42
+ "hash": "0xcda6ccaeb5d52dd1"
43
43
  }
44
44
  ],
45
45
  "claude": {
@@ -59,7 +59,7 @@
59
59
  },
60
60
  {
61
61
  "path": ".claude/hooks/pm-prompts.mjs",
62
- "hash": "0x988aed96a8073727"
62
+ "hash": "0xf4266eea17b3b9c1"
63
63
  },
64
64
  {
65
65
  "path": ".claude/settings.json",
@@ -107,7 +107,7 @@
107
107
  },
108
108
  {
109
109
  "path": "doc/guides/develop-guide.md",
110
- "hash": "0x39718de56fdb48f0"
110
+ "hash": "0xed4328d138fe76ef"
111
111
  },
112
112
  {
113
113
  "path": "doc/guides/develop-without-ai-guide.md",
@@ -47,6 +47,8 @@ Use the following skills in Claude
47
47
  When the repo belongs to a project management repo, every prompt you write
48
48
  to Claude Code is kept there, under `doc/prompts/<your first name>/` on the
49
49
  branch `prompts`. Only the prompts, not the answers. Tokens and passwords
50
- are replaced by `[redacted]`. Your first name comes from the `Git` line of
51
- `doc/team/people.md` in the project management repo; add your git email
52
- there, or your prompts land in `doc/prompts/_unknown/`.
50
+ are replaced by `[redacted]`. Your first name comes from
51
+ `doc/team/people.md` in the project management repo: the person whose
52
+ first name is your git user name or its first word, or whose `Git` line
53
+ lists your git user name. Otherwise your prompts land in
54
+ `doc/prompts/_unknown/`. Your e-mail address never goes there.
@@ -238,7 +238,7 @@ export function onPrompt(input) {
238
238
  started: new Date().toISOString(),
239
239
  folder: path.basename(projectDir),
240
240
  repos,
241
- email: git(['-C', projectDir, 'config', 'user.email']).out,
241
+ name: git(['-C', projectDir, 'config', 'user.name']).out,
242
242
  targets,
243
243
  });
244
244
  }
@@ -299,28 +299,35 @@ export function slug(name) {
299
299
  .replace(/^-+|-+$/g, '');
300
300
  }
301
301
 
302
- /** The first name in [peopleMd] whose `Git` line lists [email]. */
303
- export function personOf(email, peopleMd) {
304
- if (!email) return '';
302
+ /**
303
+ * The first name in [peopleMd] for the git user name [gitName]: the person
304
+ * whose `Git` line lists it, else the person whose first name is the git
305
+ * name or its first word. E-mail addresses play no part.
306
+ */
307
+ export function personOf(gitName, peopleMd) {
308
+ const wanted = (gitName ?? '').trim().toLowerCase();
309
+ if (!wanted) return '';
310
+ const byFirstName = new Map();
305
311
  let name = '';
306
312
  for (const line of peopleMd.split(/\r?\n/)) {
307
313
  const heading = line.match(/^##\s+(.+?)\s*$/);
308
314
  if (heading) {
309
315
  name = heading[1];
316
+ byFirstName.set(name.toLowerCase(), name);
310
317
  continue;
311
318
  }
312
- const emails = line.match(/^\s*-\s*Git:\s*(.+)$/i);
319
+ const names = line.match(/^\s*-\s*Git:\s*(.+)$/i);
313
320
  if (
314
321
  name &&
315
- emails &&
316
- emails[1]
317
- .split(/[,;\s]+/)
318
- .some((e) => e.toLowerCase() === email.toLowerCase())
322
+ names &&
323
+ names[1].split(/[,;]/).some((n) => n.trim().toLowerCase() === wanted)
319
324
  ) {
320
325
  return name;
321
326
  }
322
327
  }
323
- return '';
328
+ return (
329
+ byFirstName.get(wanted) ?? byFirstName.get(wanted.split(/\s+/)[0]) ?? ''
330
+ );
324
331
  }
325
332
 
326
333
  /** Where the prompts of a session go in the project management repo. */
@@ -336,7 +343,7 @@ export function renderHead(meta, person) {
336
343
  const lines = [
337
344
  `# ${localDate(meta.started)} · ${meta.folder} · ${String(meta.session).slice(0, 8)}`,
338
345
  '',
339
- `- Person: ${person || meta.email || 'unknown'}`,
346
+ `- Person: ${person || meta.name || 'unknown'}`,
340
347
  ];
341
348
  const repos = meta.repos ?? [];
342
349
  if (repos.length === 1 && repos[0] === meta.folder) {
@@ -399,16 +406,15 @@ function fetchBranch(dir) {
399
406
  return git(['-C', dir, 'rev-parse', ref]).out;
400
407
  }
401
408
 
402
- function identityEnv(dir) {
403
- const env = { ...gitEnv };
404
- if (!git(['-C', dir, 'config', 'user.name']).out) {
405
- env.GIT_AUTHOR_NAME = env.GIT_COMMITTER_NAME = 'pm-prompts';
406
- }
407
- if (!git(['-C', dir, 'config', 'user.email']).out) {
408
- env.GIT_AUTHOR_EMAIL = env.GIT_COMMITTER_EMAIL = 'pm-prompts@localhost';
409
- }
410
- return env;
411
- }
409
+ // The prompts are committed under a neutral identity, so the e-mail address
410
+ // of the developer never reaches the project management repo.
411
+ const identityEnv = {
412
+ ...gitEnv,
413
+ GIT_AUTHOR_NAME: 'pm-prompts',
414
+ GIT_COMMITTER_NAME: 'pm-prompts',
415
+ GIT_AUTHOR_EMAIL: 'pm-prompts@localhost',
416
+ GIT_COMMITTER_EMAIL: 'pm-prompts@localhost',
417
+ };
412
418
 
413
419
  // Commits the not yet pushed prompts of [sessions] to the prompts branch of
414
420
  // [url]. Returns how many prompts of each session the branch now holds.
@@ -421,7 +427,7 @@ function pushTarget(url, sessions) {
421
427
  os.tmpdir(),
422
428
  `pm-prompts-${process.pid}-${Date.now()}.index`,
423
429
  );
424
- const env = { ...identityEnv(dir), GIT_INDEX_FILE: index };
430
+ const env = { ...identityEnv, GIT_INDEX_FILE: index };
425
431
  try {
426
432
  const read = base
427
433
  ? git(['-C', dir, 'read-tree', base], { env })
@@ -433,8 +439,8 @@ function pushTarget(url, sessions) {
433
439
  const fresh = session.prompts.slice(pushed);
434
440
  done.set(file, session.prompts.length);
435
441
  if (!fresh.length) continue;
436
- const person = personOf(session.meta.email, people);
437
- names.add(person || session.meta.email || 'unknown');
442
+ const person = personOf(session.meta.name, people);
443
+ names.add(person || session.meta.name || 'unknown');
438
444
  const target = promptsPath(session.meta, person);
439
445
  const existing = base
440
446
  ? git(['-C', dir, 'cat-file', '-p', `${base}:${target}`])
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ggdna/dna-gg",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "DNA layer: gg CLI workflow guides, repo setup scripts and CI check",
5
5
  "keywords": [
6
6
  "dna",