djournal 0.2.0 → 0.3.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
@@ -208,6 +208,7 @@ path when they need canonical journal memory.
208
208
  djournal status
209
209
  djournal doctor
210
210
  djournal config sync.enabled true
211
+ djournal share --all
211
212
  djournal share
212
213
  djournal sync
213
214
  djournal upgrade
package/bin/journal.js CHANGED
@@ -25,7 +25,7 @@ function usage() {
25
25
  journal status [--target DIR] [--json]
26
26
  journal doctor [--target DIR] [--json]
27
27
  journal config [--target DIR] [KEY [VALUE]] [--dry-run] [--json]
28
- journal share [--target DIR] [--work SLUG] [--dry-run] [--json]
28
+ journal share [--target DIR] [--work SLUG | --all] [--dry-run] [--json]
29
29
  journal sync [--target DIR] [--work SLUG] [--dry-run] [--json]
30
30
 
31
31
  Options:
@@ -34,7 +34,7 @@ Options:
34
34
  --json Emit JSON
35
35
  --work SLUG Select a journal work item instead of active state
36
36
  --harness LIST Comma-separated codex,claude-code selection
37
- --all Select or remove every supported harness
37
+ --all Select all work items for share, or every harness
38
38
  --instructions-only Install core instructions without harness hooks
39
39
  `;
40
40
  }
@@ -72,6 +72,10 @@ function parseArgs(argv) {
72
72
  else throw new InstallerError(`unknown option: ${arg}`, "USAGE");
73
73
  }
74
74
  if (options.all && options.harnesses.length) throw new InstallerError("use either --all or --harness", "USAGE");
75
+ if (options.all && options.work) throw new InstallerError("use either --all or --work", "USAGE");
76
+ if (options.all && !["install", "uninstall", "share"].includes(command)) {
77
+ throw new InstallerError(`--all is not supported for ${command}`, "USAGE");
78
+ }
75
79
  if (options.instructionsOnly && (options.all || options.harnesses.length)) {
76
80
  throw new InstallerError("--instructions-only cannot be combined with harness selection", "USAGE");
77
81
  }
@@ -92,6 +96,12 @@ function print(value, json) {
92
96
  if (value.harnesses) process.stdout.write(`harnesses: ${value.harnesses.join(", ") || "instructions-only"}\n`);
93
97
  if (value.key) process.stdout.write(`${value.key}: ${JSON.stringify(value.value)}\n`);
94
98
  else if (value.config) process.stdout.write(`${JSON.stringify(value.config, null, 2)}\n`);
99
+ if (value.workItems) {
100
+ for (const item of value.workItems) {
101
+ process.stdout.write(`${item.changed ? "shared" : "unchanged"} ${item.work}\n`);
102
+ if (item.warning) process.stdout.write(`warning: ${item.warning}\n`);
103
+ }
104
+ }
95
105
  if (value.conflicts?.length) process.stdout.write(`conflicts: ${value.conflicts.join(", ")}\n`);
96
106
  if (value.files) {
97
107
  for (const file of value.files) process.stdout.write(`${file.status.padEnd(8)} ${file.path}\n`);
@@ -28,6 +28,16 @@ Mark a specific work item as shared:
28
28
  djournal share --work 2026-07-03-01-git-backed-journal-collaboration
29
29
  ```
30
30
 
31
+ Mark every current work item as shared:
32
+
33
+ ```bash
34
+ djournal share --all
35
+ ```
36
+
37
+ Already-shared work keeps its original sharing record. `--all` marks only work
38
+ items that currently exist in the canonical project store; it does not change
39
+ the default for future work.
40
+
31
41
  Dry run:
32
42
 
33
43
  ```bash
@@ -556,6 +556,19 @@ function readActiveWork(context, requested) {
556
556
  return { slug, relative, file, text, metadata, visibility: metadata.visibility || "local_only" };
557
557
  }
558
558
 
559
+ function readAllWork(context) {
560
+ const workRoot = path.join(context.journalRoot, "work");
561
+ const entries = fs.existsSync(workRoot) ? fs.readdirSync(workRoot, { withFileTypes: true }) : [];
562
+ const workItems = entries
563
+ .filter((entry) => entry.isDirectory() && !entry.isSymbolicLink())
564
+ .map((entry) => entry.name)
565
+ .filter((slug) => fs.existsSync(path.join(workRoot, slug, "work.md")))
566
+ .sort()
567
+ .map((slug) => readActiveWork(context, slug));
568
+ if (!workItems.length) throw new InstallerError("no journal work items found", "NO_ACTIVE_WORK");
569
+ return workItems;
570
+ }
571
+
559
572
  function readJournalConfig(target) {
560
573
  const context = projectContext(target);
561
574
  return readProjectConfig(context);
@@ -638,29 +651,43 @@ function share(options) {
638
651
  const target = checkTarget(options.target || process.cwd());
639
652
  const context = projectContext(target, options);
640
653
  const config = readProjectConfig(context);
641
- const work = readActiveWork(context, options.work);
642
654
  const projectionRoot = projectionRootFor(target, config);
643
- const ignored = config.sync?.mode === "colocated" ? gitIgnored(projectionRoot, `.journal/work/${work.slug}`, options) : false;
644
- const warning = ignored
645
- ? `.journal/work/${work.slug} appears to be ignored by Git; update .gitignore or force-add it before expecting colocated Git commits to share this work.`
646
- : undefined;
647
655
  const shared = sharedWorkItems(config);
648
- if (Object.prototype.hasOwnProperty.call(shared, work.slug)) {
649
- return { action: "share", target, projectKey: context.projectKey, work: work.slug, changed: false, shared: true, gitIgnored: ignored, warning };
656
+ const workItems = options.all ? readAllWork(context) : [readActiveWork(context, options.work)];
657
+ const sharedAt = new Date().toISOString();
658
+ const sharedBy = process.env.JOURNAL_USER_ID || runGitConfigEmail(options) || `${os.userInfo().username}@local`;
659
+ const nextShared = { ...shared };
660
+ const results = workItems.map((work) => {
661
+ const ignored = config.sync?.mode === "colocated" ? gitIgnored(projectionRoot, `.journal/work/${work.slug}`, options) : false;
662
+ const warning = ignored
663
+ ? `.journal/work/${work.slug} appears to be ignored by Git; update .gitignore or force-add it before expecting colocated Git commits to share this work.`
664
+ : undefined;
665
+ const changed = !Object.prototype.hasOwnProperty.call(shared, work.slug);
666
+ if (changed) nextShared[work.slug] = { sharedAt, sharedBy };
667
+ return { work: work.slug, changed, shared: true, gitIgnored: ignored, warning };
668
+ });
669
+ const changed = results.some((result) => result.changed);
670
+ if (!options.all) {
671
+ const result = results[0];
672
+ if (!changed) return { action: "share", target, projectKey: context.projectKey, ...result };
650
673
  }
651
674
  const next = mergePlainObject(config, {
652
675
  sharing: {
653
- sharedWorkItems: {
654
- ...shared,
655
- [work.slug]: {
656
- sharedAt: new Date().toISOString(),
657
- sharedBy: process.env.JOURNAL_USER_ID || runGitConfigEmail(options) || `${os.userInfo().username}@local`,
658
- },
659
- },
676
+ sharedWorkItems: nextShared,
660
677
  },
661
678
  });
662
- if (!options.dryRun) writeProjectConfig(context, next);
663
- return { action: "share", target, projectKey: context.projectKey, work: work.slug, changed: true, shared: true, dryRun: !!options.dryRun, gitIgnored: ignored, warning };
679
+ if (changed && !options.dryRun) writeProjectConfig(context, next);
680
+ if (!options.all) return { action: "share", target, projectKey: context.projectKey, ...results[0], dryRun: !!options.dryRun };
681
+ return {
682
+ action: "share",
683
+ target,
684
+ projectKey: context.projectKey,
685
+ all: true,
686
+ changed,
687
+ shared: true,
688
+ workItems: results,
689
+ dryRun: !!options.dryRun,
690
+ };
664
691
  }
665
692
 
666
693
  function runGitConfigEmail(options = {}) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "djournal",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Durable project memory for coding agents, stored as linked Markdown",
5
5
  "type": "commonjs",
6
6
  "bin": {